class Window(QDialog): def __init__(self): QDialog.__init__(self) self.setWindowTitle("Printen grafiek magazijnvoorraad bedragen") self.setWindowIcon(QIcon('./images/logos/logo.jpg')) self.setWindowFlags(self.windowFlags() | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint) self.chart = QChart() self.chart_view = QChartView(self.chart) self.chart_view.setRenderHint(QPainter.Antialiasing) self.buttonPreview = QPushButton('Afdrukvoorbeeld', self) self.buttonPreview.setStyleSheet( "color: black; background-color: gainsboro") self.buttonPreview.clicked.connect(self.handle_preview) self.buttonPrint = QPushButton('Printen', self) self.buttonPrint.setStyleSheet( "color: black; background-color: gainsboro") self.buttonPrint.clicked.connect(self.handle_print) self.sluiten = QPushButton('Sluiten', self) self.sluiten.setStyleSheet( "color: black; background-color: gainsboro") self.sluiten.clicked.connect(lambda: sluiten(self, m_email)) layout = QGridLayout(self) layout.addWidget(self.buttonPrint, 1, 1) layout.addWidget(self.sluiten, 1, 0) layout.addWidget(self.chart_view, 0, 0, 1, 3) layout.addWidget(self.buttonPreview, 1, 2) self.create_chart() def create_chart(self): self.chart.setTitle('Grafiek financieën magazijnvoorraad') font = QFont("Sans Serif", 10) font.setWeight(QFont.Bold) self.chart.setTitleFont(font) set0 = QBarSet('Totaal') set1 = QBarSet('Courant') set2 = QBarSet('Incourant') set0 << rpvrd[0][1] << rpvrd[1][1] << rpvrd[2][1] << rpvrd[3][ 1] << rpvrd[4][1] << rpvrd[5][1] << rpvrd[6][1] << rpvrd[7][ 1] << rpvrd[8][1] << rpvrd[9][1] << rpvrd[10][1] << rpvrd[ 11][1] set1 << rpvrd[0][2] << rpvrd[1][2] << rpvrd[2][2] << rpvrd[3][ 2] << rpvrd[4][2] << rpvrd[5][2] << rpvrd[6][2] << rpvrd[7][ 2] << rpvrd[8][2] << rpvrd[9][2] << rpvrd[10][2] << rpvrd[ 11][2] set2 << rpvrd[0][3] << rpvrd[1][3] << rpvrd[2][3] << rpvrd[3][ 3] << rpvrd[4][3] << rpvrd[5][3] << rpvrd[6][3] << rpvrd[7][ 3] << rpvrd[8][3] << rpvrd[9][3] << rpvrd[10][3] << rpvrd[ 11][3] barseries = QBarSeries() barseries.append(set0) barseries.append(set1) barseries.append(set2) categories = [rpvrd[0][0],rpvrd[1][0],rpvrd[2][0],rpvrd[3][0],rpvrd[4][0],\ rpvrd[5][0],rpvrd[6][0],rpvrd[7][0],rpvrd[8][0],rpvrd[9][0],\ rpvrd[10][0],rpvrd[11][0]] self.chart.addSeries(barseries) self.chart.axisX() self.chart.createDefaultAxes() axisX = QBarCategoryAxis() axisX.append(categories) axisX.setTitleText('Voortschrijdende periode over 12 maanden') self.chart.setAxisX(axisX, barseries) axisX.setRange(rpvrd[0][0], rpvrd[11][0]) axisX.setLabelsAngle(-90) def handle_print(self): printer = QPrinter(QPrinter.HighResolution) printer.setOrientation(QPrinter.Landscape) dialog = QPrintDialog(printer, self) if dialog.exec_() == QPrintDialog.Accepted: self.handle_paint_request(printer) def handle_preview(self): dialog = QPrintPreviewDialog() dialog.setWindowFlags(self.windowFlags() | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint) dialog.setWindowIcon(QIcon('./images/logos/logo.jpg')) dialog.setWindowFlags(dialog.windowFlags() | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint) dialog.resize(1200, 800) #dialog.setMinimumSize(1200, 800) dialog.paintRequested.connect(self.handle_paint_request) dialog.exec_() def handle_paint_request(self, printer): printer.setPageOrientation(QPageLayout.Landscape) painter = QPainter(printer) painter.setViewport(self.chart_view.rect()) painter.setWindow(self.chart_view.rect()) self.chart_view.render(painter) painter.end()
class Window(QDialog): def __init__(self): QDialog.__init__(self) self.setWindowTitle( "Printen grafiek aantal externe werken per status over jaar " + jrwk[0:4]) self.setWindowIcon(QIcon('./images/logos/logo.jpg')) self.setWindowFlags(self.windowFlags() | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint) self.chart = QChart() self.chart_view = QChartView(self.chart) self.chart_view.setRenderHint(QPainter.Antialiasing) self.buttonPreview = QPushButton('Afdrukvoorbeeld', self) self.buttonPreview.clicked.connect(self.handle_preview) self.buttonPreview.setStyleSheet( "color: navy; background-color: gainsboro") self.buttonPrint = QPushButton('Printen', self) self.buttonPrint.clicked.connect(self.handle_print) self.buttonPrint.setStyleSheet( "color: navy; background-color: gainsboro") self.buttonSluit = QPushButton('Sluiten', self) self.buttonSluit.clicked.connect(lambda: sluit(self, m_email)) self.buttonSluit.setStyleSheet( "color: navy; background-color: gainsboro") self.buttonInfo = QPushButton('Info', self) self.buttonInfo.clicked.connect(lambda: info(self)) self.buttonInfo.setStyleSheet( "color: navy; background-color: gainsboro") layout = QGridLayout(self) layout.addWidget(self.chart_view, 0, 0, 1, 4) layout.addWidget(self.buttonInfo, 1, 0) layout.addWidget(self.buttonSluit, 1, 1) layout.addWidget(self.buttonPrint, 1, 2) layout.addWidget(self.buttonPreview, 1, 3) self.create_chart() def create_chart(self): self.chart.setTitle( 'Grafiek aantal externe werken per status - opnameweek ' + jrwk) font = QFont("Sans Serif", 10) font.setWeight(QFont.Bold) self.chart.setTitleFont(font) set0 = QBarSet('Aantal externe werken per status') set0 << rpr[0][2] << rpr[1][2] << rpr[2][2] << rpr[3][2] << rpr[4][ 2] << rpr[5][2] << rpr[6][2] << rpr[7][2] barseries = QBarSeries() barseries.append(set0) barseries.append categories = [ "Status A", "Status B", "Status C", "Status D", "Status E", "Status F", "Status G", "Status H" ] self.chart.addSeries(barseries) self.chart.axisX() self.chart.createDefaultAxes() axisX = QBarCategoryAxis() axisX.append(categories) self.chart.setAxisX(axisX, barseries) axisX.setRange(str("Status A"), str("Status H")) def handle_print(self): printer = QPrinter(QPrinter.HighResolution) printer.setOrientation(QPrinter.Landscape) dialog = QPrintDialog(printer, self) if dialog.exec_() == QPrintDialog.Accepted: self.handle_paint_request(printer) def handle_preview(self): dialog = QPrintPreviewDialog() dialog.setWindowIcon(QIcon('./images/logos/logo.jpg')) dialog.setWindowFlags(dialog.windowFlags() | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint) dialog.resize(1485, 990) #dialog.setMinimumSize(1485, 990) dialog.paintRequested.connect(self.handle_paint_request) dialog.exec_() def handle_paint_request(self, printer): painter = QPainter(printer) painter.setViewport(self.chart_view.rect()) painter.setWindow(self.chart_view.rect()) printer.setPageOrientation(QPageLayout.Landscape) self.chart_view.render(painter) painter.end()
def addSeries( self, _x2idx: typing.Dict, _idx2x: list, _chart: QChart, _axis_x: QValueAxis, _axis_y: QValueAxis ): series = QLineSeries() series.setName(self.name) for x, y in zip(self.x_list, self.y_list): series.append(_x2idx[x], y) if self.color is not None: series.setColor(self.color) _chart.addSeries(series) _chart.setAxisX(_axis_x, series) _chart.setAxisY(_axis_y, series) if self.show_value: self.createShow()
def addSeries( self, _x2idx: typing.Dict, _idx2x: list, _chart: QChart, _axis_x: QValueAxis, _axis_y: QValueAxis ): bar_set = QBarSet(self.name) tmp_dict = dict(zip(self.x_list, self.y_list)) for k in _idx2x: if k in tmp_dict.keys(): bar_set.append(tmp_dict[k]) else: bar_set.append(0) if self.color is not None: bar_set.setColor(self.color) bar_series = QBarSeries() bar_series.append(bar_set) _chart.addSeries(bar_series) _chart.setAxisX(_axis_x, bar_series) _chart.setAxisY(_axis_y, bar_series) if self.show_value: self.createShow()
def addSeries( self, _x2idx: typing.Dict, _idx2x: list, _chart: QChart, _axis_x: QValueAxis, _axis_y: QValueAxis ): series = QCandlestickSeries() series.setName(self.name) for x, y in zip(self.x_list, self.y_list): series.append(QCandlestickSet(*y, _x2idx[x])) if self.inc_color is not None: series.setIncreasingColor(self.inc_color) else: series.setIncreasingColor(QColor('#c41919')) if self.dec_color is not None: series.setDecreasingColor(self.dec_color) else: series.setDecreasingColor(QColor('#009f9f')) _chart.addSeries(series) _chart.setAxisX(_axis_x, series) _chart.setAxisY(_axis_y, series) if self.show_value: self.createShow()
class LineChartView(QChartView): def __init__(self): super(LineChartView, self).__init__() self.resize(800, 600) self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 # 自定义x轴label def CreatePlot(self, NumSection, Alltime, PlotData, type, name='列车调度'): if type == 1: self.initChart(NumSection, Alltime, PlotData, name) else: self.initProgressChart(NumSection, Alltime, PlotData, name) # 提示widget self.toolTipWidget = GraphicsProxyWidget(self._chart) # line self.lineItem = QGraphicsLineItem(self._chart) pen = QPen(Qt.gray) pen.setWidth(1) self.lineItem.setPen(pen) self.lineItem.setZValue(998) #self.lineItem.hide() self.lineItem.show() # 一些固定计算,减少mouseMoveEvent中的计算量 # 获取x和y轴的最小最大值 axisX, axisY = self._chart.axisX(), self._chart.axisY() self.min_x, self.max_x = axisX.min(), axisX.max() self.min_y, self.max_y = axisY.min(), axisY.max() def resizeEvent(self, event): super(LineChartView, self).resizeEvent(event) # 当窗口大小改变时需要重新计算 # 坐标系中左上角顶点 self.point_top = self._chart.mapToPosition( QPointF(self.min_x, self.max_y)) # 坐标原点坐标 self.point_bottom = self._chart.mapToPosition( QPointF(self.min_x, self.min_y)) self.step_x = (self.max_x - self.min_x) / (self._chart.axisX().tickCount() - 1) def mouseMoveEvent(self, event): super(LineChartView, self).mouseMoveEvent(event) pos = event.pos() # 把鼠标位置所在点转换为对应的xy值 x = self._chart.mapToValue(pos).x() y = self._chart.mapToValue(pos).y() index = round(int(x / self.step_x) * self.step_x) + round( x % self.step_x) - 1 #index = round((x - self.min_x) / self.step_x) # 得到在坐标系中的所有正常显示的series的类型和点 points = [ (serie, serie.at(index)) for serie in self._chart.series() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y ] if points: pos_x = self._chart.mapToPosition( QPointF(index * self.step_x + self.min_x, self.min_y)) self.lineItem.setLine(pos_x.x(), self.point_top.y(), pos_x.x(), self.point_bottom.y()) self.lineItem.show() try: title = "" except: title = "" t_width = self.toolTipWidget.width() t_height = self.toolTipWidget.height() # 如果鼠标位置离右侧的距离小于tip宽度 x = pos.x() - t_width if self.width() - \ pos.x() - 20 < t_width else pos.x() # 如果鼠标位置离底部的高度小于tip高度 y = pos.y() - t_height if self.height() - \ pos.y() - 20 < t_height else pos.y() self.toolTipWidget.show(title, points, QPoint(x, y)) else: self.toolTipWidget.hide() self.lineItem.hide() def handleMarkerClicked(self): marker = self.sender() # 信号发送者 if not marker: return visible = not marker.series().isVisible() # # 隐藏或显示series marker.series().setVisible(visible) marker.setVisible(True) # 要保证marker一直显示 # 透明度 alpha = 1.0 if visible else 0.4 # 设置label的透明度 brush = marker.labelBrush() color = brush.color() color.setAlphaF(alpha) brush.setColor(color) marker.setLabelBrush(brush) # 设置marker的透明度 brush = marker.brush() color = brush.color() color.setAlphaF(alpha) brush.setColor(color) marker.setBrush(brush) # 设置画笔透明度 pen = marker.pen() color = pen.color() color.setAlphaF(alpha) pen.setColor(color) marker.setPen(pen) def handleMarkerHovered(self, status): # 设置series的画笔宽度 marker = self.sender() # 信号发送者 if not marker: return series = marker.series() if not series: return pen = series.pen() if not pen: return pen.setWidth(pen.width() + (1 if status else -1)) series.setPen(pen) def handleSeriesHoverd(self, point, state): # 设置series的画笔宽度 series = self.sender() # 信号发送者 pen = series.pen() if not pen: return pen.setWidth(pen.width() + (1 if state else -1)) series.setPen(pen) def initProgressChart(self, MaxTime, Allgen, PlotData, name): MinTime = 100000 for k in PlotData: if min(k[1]) < MinTime: MinTime = min(k[1]) self._chart = QChart(title=name) self._chart.setAcceptHoverEvents(True) # Series动画 self._chart.setAnimationOptions(QChart.SeriesAnimations) # dataTable = [ # ["邮件营销", [120, 132, 101, 134, 90, 230]], # ["联盟广告", [220, 182, 191, 234, 290, 330, 310]], # ["视频广告", [150, 232, 201, 154, 190, 330, 410]], # ["直接访问", [320, 332, 301, 334, 390, 330, 320]], # ["搜索引擎", [820, 932, 901, 934, 1290, 1330, 1320]] # ] for series_name, data_list, label_list in PlotData: series = QLineSeries(self._chart) for j, v in zip(label_list, data_list): series.append(j, v) series.setName(series_name) series.setPointsVisible(True) # 显示圆点 series.hovered.connect(self.handleSeriesHoverd) # 鼠标悬停 self._chart.addSeries(series) self._chart.createDefaultAxes() # 创建默认的轴 axisX = self._chart.axisX() # x轴 axisX.setTickCount(11) # x轴设置7个刻度 axisX.setGridLineVisible(False) # 隐藏从x轴往上的线条 axisY = self._chart.axisY() axisY.setTickCount(10) # y轴设置7个刻度 #axisY.setRange(0, MaxTime) # 设置y轴范围 # 自定义x轴 # axis_x = QCategoryAxis( # self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue) # axis_x.setTickCount(Allgen) # axis_x.setGridLineVisible(False) # min_x = axisX.min() # for i in range(0, Allgen + 1): # axis_x.append(str(i), min_x + i) # self._chart.setAxisX(axis_x, self._chart.series()[-1]) # 自定义y轴 # axis_y = QCategoryAxis( # self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue) # axis_y.setTickCount(round(MaxTime)-max(round(MinTime)-10,0)) # axis_y.setRange(max(round(MinTime)-10,0), round(MaxTime)) # for i in range(max(round(MinTime)-10,0), round(MaxTime) + 1): # axis_y.append('%i' % i, i) # self._chart.setAxisY(axis_y, self._chart.series()[-1]) # chart的图例 legend = self._chart.legend() # 设置图例由Series来决定样式 legend.setMarkerShape(QLegend.MarkerShapeFromSeries) # 遍历图例上的标记并绑定信号 for marker in legend.markers(): # 点击事件 marker.clicked.connect(self.handleMarkerClicked) # 鼠标悬停事件 marker.hovered.connect(self.handleMarkerHovered) self.setChart(self._chart) def initChart(self, NumSection, Alltime, PlotData, name): self._chart = QChart(title=name) self._chart.setAcceptHoverEvents(True) # Series动画 self._chart.setAnimationOptions(QChart.SeriesAnimations) # dataTable = [ # ["邮件营销", [120, 132, 101, 134, 90, 230]], # ["联盟广告", [220, 182, 191, 234, 290, 330, 310]], # ["视频广告", [150, 232, 201, 154, 190, 330, 410]], # ["直接访问", [320, 332, 301, 334, 390, 330, 320]], # ["搜索引擎", [820, 932, 901, 934, 1290, 1330, 1320]] # ] for series_name, data_list, label_list in PlotData: series = QLineSeries(self._chart) for j, v in zip(label_list, data_list): series.append(j, v) series.setName(series_name) series.setPointsVisible(True) # 显示圆点 series.hovered.connect(self.handleSeriesHoverd) # 鼠标悬停 self._chart.addSeries(series) self._chart.createDefaultAxes() # 创建默认的轴 axisX = self._chart.axisX() # x轴 axisX.setTickCount(Alltime) # x轴设置7个刻度 axisX.setGridLineVisible(False) # 隐藏从x轴往上的线条 axisY = self._chart.axisY() axisY.setTickCount(NumSection) # y轴设置7个刻度 axisY.setRange(0, NumSection) # 设置y轴范围 # 自定义x轴 axis_x = QCategoryAxis( self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue) axis_x.setTickCount(Alltime + 1) axis_x.setGridLineVisible(False) min_x = axisX.min() for i in range(0, Alltime + 1): axis_x.append(str(i), min_x + i) self._chart.setAxisX(axis_x, self._chart.series()[-1]) # 自定义y轴 axis_y = QCategoryAxis( self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionCenter) axis_y.setTickCount(NumSection) axis_y.setRange(0, NumSection) for i in range(0, NumSection + 1): axis_y.append('section%i' % (NumSection - i + 1), i) self._chart.setAxisY(axis_y, self._chart.series()[-1]) # chart的图例 legend = self._chart.legend() # 设置图例由Series来决定样式 legend.setMarkerShape(QLegend.MarkerShapeFromSeries) # 遍历图例上的标记并绑定信号 for marker in legend.markers(): # 点击事件 marker.clicked.connect(self.handleMarkerClicked) # 鼠标悬停事件 marker.hovered.connect(self.handleMarkerHovered) self.setChart(self._chart)
class PlotterPane(QChartView): """ This plotter widget makes viewing sensor data easy! This widget represents a chart that will look for tuple data from the MicroPython REPL, Python 3 REPL or Python 3 code runner and will auto-generate a graph. """ data_flood = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) # Holds the raw input to be checked for actionable data to display. self.input_buffer = [] # Holds the raw actionable data detected while plotting. self.raw_data = [] self.setObjectName("plotterpane") self.max_x = 100 # Maximum value along x axis self.max_y = 1000 # Maximum value +/- along y axis self.flooded = False # Flag to indicate if data flooding is happening. # Holds deques for each slot of incoming data (assumes 1 to start with) self.data = [deque([0] * self.max_x)] # Holds line series for each slot of incoming data (assumes 1 to start # with). self.series = [QLineSeries()] # Ranges used for the Y axis (up to 1000, after which we just double # the range). self.y_ranges = [1, 5, 10, 25, 50, 100, 250, 500, 1000] # Set up the chart with sensible defaults. self.chart = QChart() self.chart.legend().hide() self.chart.addSeries(self.series[0]) self.axis_x = QValueAxis() self.axis_y = QValueAxis() self.axis_x.setRange(0, self.max_x) self.axis_y.setRange(-self.max_y, self.max_y) self.axis_x.setLabelFormat("time") self.axis_y.setLabelFormat("%d") self.chart.setAxisX(self.axis_x, self.series[0]) self.chart.setAxisY(self.axis_y, self.series[0]) self.setChart(self.chart) self.setRenderHint(QPainter.Antialiasing) def process_tty_data(self, data): """ Takes raw bytes and, if a valid tuple is detected, adds the data to the plotter. The the length of the bytes data > 1024 then a data_flood signal is emitted to ensure Mu can take action to remain responsive. """ # Data flooding guards. if self.flooded: return if len(data) > 1024: self.flooded = True self.data_flood.emit() return data = data.replace(b"\r\n", b"\n") self.input_buffer.append(data) # Check if the data contains a Python tuple, containing numbers, on a # single line (i.e. ends with \n). input_bytes = b"".join(self.input_buffer) lines = input_bytes.split(b"\n") for line in lines: if line.startswith(b"(") and line.endswith(b")"): # Candidate tuple. Extract the raw bytes into a numeric tuple. raw_values = [val.strip() for val in line[1:-1].split(b",")] numeric_values = [] for raw in raw_values: try: numeric_values.append(int(raw)) # It worked, so move onto the next value. continue except ValueError: # Try again as a float. pass try: numeric_values.append(float(raw)) except ValueError: # Not an int or float, so ignore this value. continue if numeric_values: # There were numeric values in the tuple, so use them! self.add_data(tuple(numeric_values)) # Reset the input buffer. self.input_buffer = [] if lines[-1]: # Append any bytes that are not yet at the end of a line, for # processing next time we read data from self.connection. self.input_buffer.append(lines[-1]) def add_data(self, values): """ Given a tuple of values, ensures there are the required number of line series, add the data to the line series, update the range of the chart so the chart displays nicely. """ # Store incoming data to dump as CSV at the end of the session. self.raw_data.append(values) # Check the number of incoming values. if len(values) != len(self.series): # Adjust the number of line series. value_len = len(values) series_len = len(self.series) if value_len > series_len: # Add new line series. for i in range(value_len - series_len): new_series = QLineSeries() self.chart.addSeries(new_series) self.chart.setAxisX(self.axis_x, new_series) self.chart.setAxisY(self.axis_y, new_series) self.series.append(new_series) self.data.append(deque([0] * self.max_x)) else: # Remove old line series. for old_series in self.series[value_len:]: self.chart.removeSeries(old_series) self.series = self.series[:value_len] self.data = self.data[:value_len] # Add the incoming values to the data to be displayed, and compute # max range. max_ranges = [] for i, value in enumerate(values): self.data[i].appendleft(value) max_ranges.append(max([max(self.data[i]), abs(min(self.data[i]))])) if len(self.data[i]) > self.max_x: self.data[i].pop() # Re-scale y-axis. max_y_range = max(max_ranges) y_range = bisect.bisect_left(self.y_ranges, max_y_range) if y_range < len(self.y_ranges): self.max_y = self.y_ranges[y_range] elif max_y_range > self.max_y: self.max_y += self.max_y elif max_y_range < self.max_y / 2: self.max_y = self.max_y / 2 self.axis_y.setRange(-self.max_y, self.max_y) # Ensure floats are used to label y axis if the range is small. if self.max_y <= 5: self.axis_y.setLabelFormat("%2.2f") else: self.axis_y.setLabelFormat("%d") # Update the line series with the data. for i, line_series in enumerate(self.series): line_series.clear() xy_vals = [] for j in range(self.max_x): val = self.data[i][self.max_x - 1 - j] xy_vals.append((j, val)) for point in xy_vals: line_series.append(*point) def set_theme(self, theme): """ Sets the theme / look for the plotter pane. """ if theme == "day": self.chart.setTheme(QChart.ChartThemeLight) elif theme == "night": self.chart.setTheme(QChart.ChartThemeDark) else: self.chart.setTheme(QChart.ChartThemeHighContrast)
class Chart(QChartView): def __init__(self): self.chart = QChart() QChartView.__init__(self, self.chart) self.axisX = QValueAxis() self.axisY = QValueAxis() self.xRange = [0, 100] self.yRange = [-100, 100] self.curve = [] self.scatterCurve = [] self.setBackgroundBrush(QColor('#D8D8D8')) self.setRenderHint(QPainter.Antialiasing) self.chart.setAnimationOptions(QChart.NoAnimation) self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignBottom) self.chart.legend().setLabelColor(Qt.white) self.chart.legend().setMarkerShape(QLegend.MarkerShapeFromSeries) self.chart.setBackgroundBrush(QColor('#00004D')) penAxisGrid = QPen(QColor('#F2F2F2')) penAxisGrid.setWidthF(0.5) penAxisMinorGrid = QPen(QColor('#A4A4A4')) penAxisMinorGrid.setWidthF(0.3) self.axisX.setGridLinePen(penAxisGrid) self.axisX.setLinePen(penAxisGrid) self.axisY.setGridLinePen(penAxisGrid) self.axisY.setLinePen(penAxisGrid) self.axisX.setMinorGridLinePen(penAxisMinorGrid) self.axisY.setMinorGridLinePen(penAxisMinorGrid) self.axisX.setLabelsColor(Qt.white) self.axisY.setLabelsColor(Qt.white) self.axisX.setMinorTickCount(4) self.axisY.setMinorTickCount(4) self.axisX.setTitleBrush(Qt.white) self.axisY.setTitleBrush(Qt.white) self.axisX.setTitleText('Time') self.axisY.setTitleText('Signal Amplitude') self.axisX.setTickCount(11) self.axisY.setTickCount(11) self.axisX.setRange(self.xRange[0], self.xRange[1]) self.axisY.setRange(self.yRange[0], self.yRange[1]) def setAddSerie(self, name, color): self.curve.append(QLineSeries()) pen = self.curve[len(self.curve) - 1].pen() pen.setColor(QColor(color)) pen.setWidthF(2) self.curve[len(self.curve) - 1].setPen(pen) self.curve[len(self.curve) - 1].setName(name) self.chart.addSeries(self.curve[len(self.curve) - 1]) self.chart.setAxisX(self.axisX, self.curve[len(self.curve) - 1]) self.chart.setAxisY(self.axisY, self.curve[len(self.curve) - 1]) def setAddScatterSerie(self, color): self.scatterCurve.append(QScatterSeries()) pen = self.scatterCurve[len(self.scatterCurve) - 1].pen() pen.setColor(QColor(color)) pen.setWidthF(1) self.scatterCurve[len(self.scatterCurve) - 1].setPen(pen) self.scatterCurve[len(self.scatterCurve) - 1].setColor(QColor(color)) self.scatterCurve[len(self.scatterCurve) - 1].setMarkerSize(10) self.chart.addSeries(self.scatterCurve[len(self.scatterCurve) - 1]) self.chart.setAxisX(self.axisX, self.scatterCurve[len(self.scatterCurve) - 1]) self.chart.setAxisY(self.axisY, self.scatterCurve[len(self.scatterCurve) - 1]) def setDataChartScatter(self, xData, yData1, yData2): if xData > self.xRange[1]: addValue = xData - self.xRange[1] if self.xRange[0] is not 0: self.xRange[0] = self.xRange[0] + addValue self.xRange[1] = self.xRange[1] + addValue self.axisX.setRange(self.xRange[0], self.xRange[1]) self.curve[0].append(xData, yData1) self.curve[1].append(xData, yData2) self.scatterCurve[0].append(xData, yData1) self.scatterCurve[1].append(xData, yData2) def setDataChart(self, xData, yData1, yData2): if xData > self.xRange[1]: addValue = xData - self.xRange[1] if self.xRange[0] is not 0: self.xRange[0] = self.xRange[0] + addValue self.xRange[1] = self.xRange[1] + addValue self.axisX.setRange(self.xRange[0], self.xRange[1]) self.curve[0].append(xData, yData1) self.curve[1].append(xData, yData2) def setRangeY(self, yRange, autoscale): if autoscale: if yRange[0] == 0 and yRange[1] == 0: yRange[0] = -0.50 yRange[1] = 0.50 elif yRange[0] == 0: yRange[0] = -0.10 yRange[1] = yRange[1] * 1.10 elif yRange[1] == 0: yRange[0] = yRange[0] * 1.10 yRange[1] = 0.10 else: if yRange[0] < 0: yRange[0] = yRange[0] * 1.10 else: yRange[0] = yRange[0] - (yRange[0] * 0.10) if yRange[1] < 0: yRange[1] = yRange[1] - (yRange[1] * 0.10) else: yRange[1] = yRange[1] * 1.10 self.axisY.setRange(yRange[0], yRange[1]) else: self.axisY.setRange(yRange[0], yRange[1]) def setRangeX(self, xRange): self.axisX.setRange(xRange[0], xRange[1]) def getRangeX(self): return self.xRange def setAxisXName(self, name): self.axisX.setTitleText(name) def setAxisYName(self, name): self.axisY.setTitleText(name) def setAxisXTickCount(self, tick): self.axisX.setTickCount(tick) def setAxisYTickCount(self, tick): self.axisY.setTickCount(tick) def initSerie(self): self.curve.clear() def initSeries(self): self.curve[0].clear() self.curve[1].clear()
class Display(QWidget): def __init__(self, trainer='MLP', debug=False): # QWidget Setup QWidget.__init__(self, flags=Qt.CustomizeWindowHint | Qt.WindowTitleHint) self.setWindowTitle("NeuroSky GUI") self.resize(1400, 1000) # Linker Params self._linker = Linker(debug=debug, trainer=trainer) self.TRAINER_FORWARD = self._linker.trainer.add_identifier('forward') self.TRAINER_BACKWARD = self._linker.trainer.add_identifier('backward') self.TRAINER_IDLE = self._linker.trainer.add_identifier('idle') # Indicators self._raw_data_indicator = self._create_indicator('Raw Data:') self._poor_level_indicator = self._create_indicator( 'Poor Level Signal:') self._sample_rate_indicator = self._create_indicator('Sample Rate:') self._prediction_indicator = self._create_indicator('Prediction:') self._training_status_indicator = self._create_indicator( 'Training Status:') self._forward_counter_indicator = self._create_indicator( 'Current Forward Count:') self._backward_counter_indicator = self._create_indicator( 'Current Backward Count:') self._idle_counter_indicator = self._create_indicator( 'Current Idle Count:') # Initializing layout self.main_page() # Series self._x_axis = 0 self._connect_data() def main_page(self): # type: (Display) -> None # Top Layout top_left_layout = QVBoxLayout() top_left_layout.addLayout(self._raw_data_indicator['layout']) top_left_layout.addLayout(self._poor_level_indicator['layout']) top_left_layout.addLayout(self._sample_rate_indicator['layout']) top_right_layout = QVBoxLayout() top_right_layout.addWidget(self._get_connector_chart(), alignment=Qt.AlignCenter) # top_right_layout.setStretchFactor(self._get_connector_chart(), 1) top_layout = QHBoxLayout() top_layout.addLayout(top_left_layout) top_layout.addLayout(top_right_layout) # Bottom Layout bottom_left_layout = QVBoxLayout() bottom_left_layout.addLayout(self._prediction_indicator['layout']) bottom_left_layout.addLayout(self._training_status_indicator['layout']) bottom_left_layout.addLayout(self._idle_counter_indicator['layout']) bottom_left_layout.addLayout(self._forward_counter_indicator['layout']) bottom_left_layout.addLayout( self._backward_counter_indicator['layout']) bottom_right_layout = QVBoxLayout() bottom_right_layout.addWidget(self._get_processor_chart(), alignment=Qt.AlignCenter) bottom_layout = QHBoxLayout() bottom_layout.addLayout(bottom_left_layout) bottom_layout.addLayout(bottom_right_layout) # Outer Layout outer_layout = QVBoxLayout() outer_layout.addLayout(top_layout) outer_layout.addLayout(bottom_layout) # Set Layout self.setLayout(outer_layout) def _get_connector_chart(self): # type: (Display) -> QChartView # Create pen pen = QLineSeries().pen() pen.setColor(Qt.blue) pen.setWidthF(1) # Series self._connector_series = QLineSeries() self._connector_series.setPen(pen) self._connector_series.useOpenGL() # Chart self._connector_chart = QChart() self._connector_chart.legend().hide() self._connector_chart.addSeries(self._connector_series) self._connector_chart.createDefaultAxes() self._connector_chart.axisX().setMax(100) self._connector_chart.axisX().setMin(0) self._connector_chart.axisY().setMax(500) self._connector_chart.axisY().setMin(-500) # Chart View view = QChartView(self._connector_chart) view.setRenderHint(QPainter.Antialiasing) view.setStyleSheet('margin: 0px; height: 250%; width: 400%;') return view def _get_processor_chart(self): # type: (Display) -> QChartView # Create pen pen = QLineSeries().pen() pen.setColor(Qt.red) pen.setWidthF(1) # Series self._processor_series = QLineSeries() self._processor_series.setPen(pen) self._processor_series.useOpenGL() # Chart self._processor_chart = QChart() self._processor_chart.legend().hide() self._processor_chart.addSeries(self._processor_series) self._processor_chart.createDefaultAxes() self._processor_chart.axisX().setMax(100) self._processor_chart.axisX().setMin(0) self._processor_chart.axisY().setMax(5000) self._processor_chart.axisY().setMin(0) self._processor_x_axis = QValueAxis() self._processor_x_axis.setLabelFormat('%i') self._processor_chart.setAxisX(self._processor_x_axis, self._processor_series) self._processor_y_axis = QLogValueAxis() self._processor_y_axis.setLabelFormat('%g') self._processor_y_axis.setBase(8) # Chart View view = QChartView(self._processor_chart) view.setRenderHint(QPainter.Antialiasing) view.setStyleSheet('margin: 0px; height: 250%; width: 400%;') return view def _connect_data(self): # type: (Display) -> None self._linker.raw.connect(self._add_connector_data) self._linker.poor_signal_level.connect( lambda level: self._poor_level_indicator['label'].setText( str(level))) self._linker.sampling_rate.connect( lambda rate: self._sample_rate_indicator['label'].setText(str(rate) )) self._linker.fft.connect(self._add_processor_data) # self._linker.prediction.connect( # lambda prediction: self._prediction_indicator['label'].setText(str(prediction)) # ) # self._linker.training_status.connect( # lambda status: self._training_status_indicator['label'].setText(str(status)) # ) # self._linker.identifiers.connect(self._connect_identifiers) def keyPressEvent(self, event): # type: (Display, {key}) -> None key = event.key() if key == Qt.Key_Escape: self._linker.close() self.close() # elif key == Qt.Key_W: # self._linker.connector.record( # './data/raw_data/' + self._linker.trainer.get_next_connector_label(self.TRAINER_FORWARD) # ) # self._linker.processor.record( # './data/processed_data/' + self._linker.trainer.get_next_processor_label(self.TRAINER_FORWARD) # ) # self._linker.trainer.train(self.TRAINER_FORWARD) # elif key == Qt.Key_S: # self._linker.connector.record( # './data/raw_data/' + self._linker.trainer.get_next_connector_label(self.TRAINER_BACKWARD) # ) # self._linker.processor.record( # './data/processed_data/' + self._linker.trainer.get_next_processor_label(self.TRAINER_BACKWARD) # ) # self._linker.trainer.train(self.TRAINER_BACKWARD) # elif key == Qt.Key_Space: # self._linker.connector.record( # './data/raw_data/' + self._linker.trainer.get_next_connector_label(self.TRAINER_IDLE) # ) # self._linker.processor.record( # './data/processed_data/' + self._linker.trainer.get_next_processor_label(self.TRAINER_IDLE) # ) # self._linker.trainer.train(self.TRAINER_IDLE) else: print(key) @staticmethod def _create_indicator( label): # type: (Any) -> Dict[str, Union[QHBoxLayout, QLabel]] layout = QHBoxLayout() display_widget = QLabel(label) layout.addWidget(display_widget, alignment=Qt.AlignCenter) label_widget = QLabel('Initializing...') layout.addWidget(label_widget, alignment=Qt.AlignCenter) return { 'layout': layout, 'display': display_widget, 'label': label_widget } @pyqtSlot(int) def _add_connector_data(self, data): # type: (Display, Any) -> Optional[Any] self._raw_data_indicator['label'].setText(str(data)) self._connector_series.append(self._x_axis, data) if self._connector_series.count() >= 100: self._connector_series.clear() self._x_axis = 0 else: self._x_axis += 1 @pyqtSlot(np.ndarray) def _add_processor_data( self, data): # type: (Display, {__getitem__}) -> Optional[Any] self._processor_series.clear() x_axis = np.fft.rfftfreq(250, 2 * (1 / 512))[2:50] y_axis = data for i in range(len(x_axis)): self._processor_series.append(x_axis[i], y_axis[i]) @pyqtSlot(list) def _connect_identifiers(self, identifiers): for identifier in identifiers: if identifier['name'] == self.TRAINER_IDLE: self._idle_counter_indicator['label'].setText( str(identifier['training_count'])) elif identifier['name'] == self.TRAINER_FORWARD: self._forward_counter_indicator['label'].setText( str(identifier['training_count'])) elif identifier['name'] == self.TRAINER_BACKWARD: self._backward_counter_indicator['label'].setText( str(identifier['training_count']))
def plotgraph(self): print(self.button_group.checkedButton().text()) collection_name = self.button_group.checkedButton().text() mycol = mydb[collection_name] font = QtGui.QFont() font.setPointSize(9) ############### Query from the SKU(collection) for total number of good/bad count ############## global y1,y2,y3,y4,y5,y6,y7,y8,z1,z2,z3,z4,z5,z6,z7,z8 ls0 =[] myquery0 = {"Status": "Bad", "SKU id":"S1" } for x in mycol.find(myquery0): ls0.append(x) y1 = len(ls0) ls1 =[] myquery1 = {"Status": "Good", "SKU id":"S1" } for x in mycol.find(myquery1): ls1.append(x) z1 = len(ls1) ls2 =[] myquery2 = {"Status": "Bad", "SKU id":"S2" } for x in mycol.find(myquery2): ls2.append(x) y2 = len(ls2) ls3 =[] myquery3 = {"Status": "Good", "SKU id":"S2" } for x in mycol.find(myquery3): ls3.append(x) z2 = len(ls3) ls4 =[] myquery4 = {"Status": "Bad", "SKU id":"S3" } for x in mycol.find(myquery4): ls4.append(x) y3 = len(ls4) ls5 =[] myquery5 = {"Status": "Good", "SKU id":"S3" } for x in mycol.find(myquery5): ls5.append(x) z3 = len(ls5) ls6 =[] myquery6 = {"Status": "Bad", "SKU id":"S4" } for x in mycol.find(myquery6): ls6.append(x) y4 = len(ls6) ls7 =[] myquery7 = {"Status": "Good", "SKU id":"S4" } for x in mycol.find(myquery7): ls7.append(x) z4 = len(ls7) ls8 =[] myquery8 = {"Status": "Bad", "SKU id":"S5" } for x in mycol.find(myquery8): ls8.append(x) y5 = len(ls8) ls9 =[] myquery9 = {"Status": "Good", "SKU id":"S5" } for x in mycol.find(myquery9): ls9.append(x) z5 = len(ls9) ls10 =[] myquery10 = {"Status": "Bad", "SKU id":"S6" } for x in mycol.find(myquery10): ls10.append(x) y6 = len(ls10) ls11 =[] myquery11 = {"Status": "Good", "SKU id":"S6" } for x in mycol.find(myquery11): ls11.append(x) z6 = len(ls11) ls12 =[] myquery12 = {"Status": "Bad", "SKU id":"S7" } for x in mycol.find(myquery12): ls12.append(x) y7 = len(ls12) ls13 =[] myquery13 = {"Status": "Good", "SKU id":"S7" } for x in mycol.find(myquery13): ls13.append(x) z7 = len(ls13) ls14 =[] myquery14 = {"Status": "Bad", "SKU id":"S8" } for x in mycol.find(myquery14): ls14.append(x) y8 = len(ls14) ls15 =[] myquery15 = {"Status": "Good", "SKU id":"S8" } for x in mycol.find(myquery15): ls15.append(x) z8 = len(ls15) self.clearLayout(lay) ######################################################################### set0 = QBarSet("Good") set1 = QBarSet("Bad") # set0.append([5400, 4900, 5100, 5400, 5000, 4800, 4500, 3000]) # set1.append([600, 800, 900, 600, 1000, 1200, 1500, 3000]) set0.append([z1,z2,z3,z4,z5,z6,z7,z8]) set1.append([y1,y2,y3,y4,y5,y6,y7,y8]) series = QStackedBarSeries() series.append(set0) series.append(set1) chart = QChart() chart.addSeries(series) chart.setTitle("Production Count") chart.setAnimationOptions(QChart.SeriesAnimations) categories = ["12:30", "13:00", "13:30", "14:00", "14:30", "15:00", "15:30", "16:00"] axis = QBarCategoryAxis() axis.append(categories) chart.createDefaultAxes() chart.setAxisX(axis, series) axisY = QValueAxis() axisY.setRange(0, 6000) axisY.setTitleText("Units") chart.setAxisY(axisY) chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) chartView = QChartView(chart) chartView.setRenderHint(QPainter.Antialiasing) chartView.setToolTip('Good Units v/s Bad Units') comboBox = QtWidgets.QComboBox() comboBox.setGeometry(QtCore.QRect(20, 20, 171, 31)) comboBox.setObjectName("comboBox") comboBox.addItem("SKU Selection") comboBox.addItem("Analytics View") comboBox.activated[str].connect(self.bargraph) comboBox.setToolTip("Select an Option!") # Message to show when mouse hover comboBox.setFont(font) lay.addWidget(comboBox,0) lay.addWidget(chartView,1)
class widgetFDChart(QtWidgets.QWidget, qdesignFDChart.Ui_Form): def __init__(self, parent=None): super(widgetFDChart, self).__init__(parent) self.setupUi(self) tribchartmenustyle(self) # SetUp for Chart to Display Distribution curves and inputs. self.chart = QChart() self.chart.legend().setVisible(False) self.chart.axisX = QValueAxis() self.chart.axisY = QValueAxis() self.chart.axisX.setTickCount(10) self.chart.axisY.setTickCount(10) self.chart.axisX.setTitleText("Value") self.chart.axisY.setTitleText("Probability") self.chartview = QChartView(self.chart) self.verticalLayout.addWidget(self.chartview) # self.setAxesMinMax(-3,3,0.01,1.5) self.chartview.setRenderHint(QPainter.Antialiasing) # self.legend().setVisible(True) # self.setAnimationOptions(QChart.SeriesAnimations) # self.legend().setAlignment(Qt.AlignBottom) # Connect Buttons self.pushButtonExportPNG.pressed.connect(self._onActionSavePNG) def axesMinMax(self): # returns a length 4 list of the axes min and max values [x1,x2,y1,y2] return [ self.chart.axisX.min(), self.chart.axisX.max(), self.chart.axisY.min(), self.chart.axisY.max() ] def setAxes(self, series): # assigns a series to the chart default axes self.chart.setAxisX(self.chart.axisX, series) self.chart.setAxisY(self.chart.axisY, series) def setAxesMinMax(self, x1, x2, y1, y2): # sets the min max values in X and Y self.chart.axisX.setMin(x1) self.chart.axisX.setMax(x2) self.chart.axisY.setMin(y1) self.chart.axisY.setMax(y2) def setTitle(self, title): self.chart.setTitle(title) def addDistrLine(self, type, n, kstats): data = distr.distrpdf(type, n, **kstats) xmin = min(data['X']) xmax = max(data['X']) ymin = min(data['Y']) ymax = max(data['Y']) self.lineDist = XLineSeries(data, xkey='X')[0] self.chart.addSeries(self.lineDist) self.setAxes(self.lineDist) xscal = 0.1 * (xmax - xmin) yscal = 0.1 * (ymax - ymin) self.setAxesMinMax(xmin - xscal, xmax + xscal, ymin, ymax + yscal) def updateChart(self, tabledata): self.activeDist = tabledata[0] self.kstats = tabledata[1] self.chart.removeAllSeries() self.addDistrLine(self.activeDist, 100, self.kstats) @pyqtSlot(str) def paintChart(self, filename): #pixmap = QPixmap() pixmap = self.chartview.grab() pixmap.save(filename) def _onActionSavePNG(self): pngfile = QtWidgets.QFileDialog.getSaveFileName( self, caption='Export PNG As', directory='~', filter='*.png') #try: self.paintChart(pngfile[0])
class ChartView(QChartView): def __init__(self, *args, **kwargs): super(ChartView, self).__init__(*args, **kwargs) self.resize(800, 600) self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 # 自定义x轴label self.category = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"] self.initChart() # 提示widget self.toolTipWidget = GraphicsProxyWidget(self._chart) # line self.lineItem = QGraphicsLineItem(self._chart) pen = QPen(Qt.gray) pen.setWidth(1) self.lineItem.setPen(pen) self.lineItem.setZValue(998) self.lineItem.hide() # 一些固定计算,减少mouseMoveEvent中的计算量 # 获取x和y轴的最小最大值 axisX, axisY = self._chart.axisX(), self._chart.axisY() self.min_x, self.max_x = axisX.min(), axisX.max() self.min_y, self.max_y = axisY.min(), axisY.max() def resizeEvent(self, event): super(ChartView, self).resizeEvent(event) # 当窗口大小改变时需要重新计算 # 坐标系中左上角顶点 self.point_top = self._chart.mapToPosition( QPointF(self.min_x, self.max_y)) # 坐标原点坐标 self.point_bottom = self._chart.mapToPosition( QPointF(self.min_x, self.min_y)) self.step_x = (self.max_x - self.min_x) / \ (self._chart.axisX().tickCount() - 1) def mouseMoveEvent(self, event): super(ChartView, self).mouseMoveEvent(event) pos = event.pos() # 把鼠标位置所在点转换为对应的xy值 x = self._chart.mapToValue(pos).x() y = self._chart.mapToValue(pos).y() index = round((x - self.min_x) / self.step_x) # 得到在坐标系中的所有正常显示的series的类型和点 points = [(serie, serie.at(index)) for serie in self._chart.series() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y] if points: pos_x = self._chart.mapToPosition( QPointF(index * self.step_x + self.min_x, self.min_y)) self.lineItem.setLine(pos_x.x(), self.point_top.y(), pos_x.x(), self.point_bottom.y()) self.lineItem.show() try: title = self.category[index] except: title = "" t_width = self.toolTipWidget.width() t_height = self.toolTipWidget.height() # 如果鼠标位置离右侧的距离小于tip宽度 x = pos.x() - t_width if self.width() - \ pos.x() - 20 < t_width else pos.x() # 如果鼠标位置离底部的高度小于tip高度 y = pos.y() - t_height if self.height() - \ pos.y() - 20 < t_height else pos.y() self.toolTipWidget.show( title, points, QPoint(x, y)) else: self.toolTipWidget.hide() self.lineItem.hide() def handleMarkerClicked(self): marker = self.sender() # 信号发送者 if not marker: return visible = not marker.series().isVisible() # # 隐藏或显示series marker.series().setVisible(visible) marker.setVisible(True) # 要保证marker一直显示 # 透明度 alpha = 1.0 if visible else 0.4 # 设置label的透明度 brush = marker.labelBrush() color = brush.color() color.setAlphaF(alpha) brush.setColor(color) marker.setLabelBrush(brush) # 设置marker的透明度 brush = marker.brush() color = brush.color() color.setAlphaF(alpha) brush.setColor(color) marker.setBrush(brush) # 设置画笔透明度 pen = marker.pen() color = pen.color() color.setAlphaF(alpha) pen.setColor(color) marker.setPen(pen) def handleMarkerHovered(self, status): # 设置series的画笔宽度 marker = self.sender() # 信号发送者 if not marker: return series = marker.series() if not series: return pen = series.pen() if not pen: return pen.setWidth(pen.width() + (1 if status else -1)) series.setPen(pen) def handleSeriesHoverd(self, point, state): # 设置series的画笔宽度 series = self.sender() # 信号发送者 pen = series.pen() if not pen: return pen.setWidth(pen.width() + (1 if state else -1)) series.setPen(pen) def initChart(self): self._chart = QChart(title="折线图堆叠") self._chart.setAcceptHoverEvents(True) # Series动画 self._chart.setAnimationOptions(QChart.SeriesAnimations) dataTable = [ ["邮件营销", [120, 132, 101, 134, 90, 230, 210]], ["联盟广告", [220, 182, 191, 234, 290, 330, 310]], ["视频广告", [150, 232, 201, 154, 190, 330, 410]], ["直接访问", [320, 332, 301, 334, 390, 330, 320]], ["搜索引擎", [820, 932, 901, 934, 1290, 1330, 1320]] ] for series_name, data_list in dataTable: series = QLineSeries(self._chart) for j, v in enumerate(data_list): series.append(j, v) series.setName(series_name) series.setPointsVisible(True) # 显示圆点 series.hovered.connect(self.handleSeriesHoverd) # 鼠标悬停 self._chart.addSeries(series) self._chart.createDefaultAxes() # 创建默认的轴 axisX = self._chart.axisX() # x轴 axisX.setTickCount(7) # x轴设置7个刻度 axisX.setGridLineVisible(False) # 隐藏从x轴往上的线条 axisY = self._chart.axisY() axisY.setTickCount(7) # y轴设置7个刻度 axisY.setRange(0, 1500) # 设置y轴范围 # 自定义x轴 axis_x = QCategoryAxis( self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue) axis_x.setTickCount(7) axis_x.setGridLineVisible(False) min_x = axisX.min() max_x = axisX.max() step = (max_x - min_x) / (7 - 1) # 7个tick for i in range(0, 7): axis_x.append(self.category[i], min_x + i * step) self._chart.setAxisX(axis_x, self._chart.series()[-1]) # chart的图例 legend = self._chart.legend() # 设置图例由Series来决定样式 legend.setMarkerShape(QLegend.MarkerShapeFromSeries) # 遍历图例上的标记并绑定信号 for marker in legend.markers(): # 点击事件 marker.clicked.connect(self.handleMarkerClicked) # 鼠标悬停事件 marker.hovered.connect(self.handleMarkerHovered) self.setChart(self._chart)
class PlotterPane(QChartView): """ This plotter widget makes viewing sensor data easy! This widget represents a chart that will look for tuple data from the MicroPython REPL, Python 3 REPL or Python 3 code runner and will auto-generate a graph. """ data_flood = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) # Holds the raw input to be checked for actionable data to display. self.input_buffer = [] # Holds the raw actionable data detected while plotting. self.raw_data = [] self.setObjectName('plotterpane') self.max_x = 100 # Maximum value along x axis self.max_y = 1000 # Maximum value +/- along y axis self.flooded = False # Flag to indicate if data flooding is happening. # Holds deques for each slot of incoming data (assumes 1 to start with) self.data = [deque([0] * self.max_x), ] # Holds line series for each slot of incoming data (assumes 1 to start # with). self.series = [QLineSeries(), ] # Ranges used for the Y axis (up to 1000, after which we just double # the range). self.y_ranges = [1, 5, 10, 25, 50, 100, 250, 500, 1000] # Set up the chart with sensible defaults. self.chart = QChart() self.chart.legend().hide() self.chart.addSeries(self.series[0]) self.axis_x = QValueAxis() self.axis_y = QValueAxis() self.axis_x.setRange(0, self.max_x) self.axis_y.setRange(-self.max_y, self.max_y) self.axis_x.setLabelFormat("time") self.axis_y.setLabelFormat("%d") self.chart.setAxisX(self.axis_x, self.series[0]) self.chart.setAxisY(self.axis_y, self.series[0]) self.setChart(self.chart) self.setRenderHint(QPainter.Antialiasing) def process_bytes(self, data): """ Takes raw bytes and, if a valid tuple is detected, adds the data to the plotter. The the length of the bytes data > 1024 then a data_flood signal is emitted to ensure Mu can take action to remain responsive. """ # Data flooding guards. if self.flooded: return if len(data) > 1024: self.flooded = True self.data_flood.emit() return data = data.replace(b'\r\n', b'\n') self.input_buffer.append(data) # Check if the data contains a Python tuple, containing numbers, on a # single line (i.e. ends with \n). input_bytes = b''.join(self.input_buffer) lines = input_bytes.split(b'\n') for line in lines: if line.startswith(b'(') and line.endswith(b')'): # Candidate tuple. Extract the raw bytes into a numeric tuple. raw_values = [val.strip() for val in line[1:-1].split(b',')] numeric_values = [] for raw in raw_values: try: numeric_values.append(int(raw)) # It worked, so move onto the next value. continue except ValueError: # Try again as a float. pass try: numeric_values.append(float(raw)) except ValueError: # Not an int or float, so ignore this value. continue if numeric_values: # There were numeric values in the tuple, so use them! self.add_data(tuple(numeric_values)) # Reset the input buffer. self.input_buffer = [] if lines[-1]: # Append any bytes that are not yet at the end of a line, for # processing next time we read data from self.serial. self.input_buffer.append(lines[-1]) def add_data(self, values): """ Given a tuple of values, ensures there are the required number of line series, add the data to the line series, update the range of the chart so the chart displays nicely. """ # Store incoming data to dump as CSV at the end of the session. self.raw_data.append(values) # Check the number of incoming values. if len(values) != len(self.series): # Adjust the number of line series. value_len = len(values) series_len = len(self.series) if value_len > series_len: # Add new line series. for i in range(value_len - series_len): new_series = QLineSeries() self.chart.addSeries(new_series) self.chart.setAxisX(self.axis_x, new_series) self.chart.setAxisY(self.axis_y, new_series) self.series.append(new_series) self.data.append(deque([0] * self.max_x)) else: # Remove old line series. for old_series in self.series[value_len:]: self.chart.removeSeries(old_series) self.series = self.series[:value_len] self.data = self.data[:value_len] # Add the incoming values to the data to be displayed, and compute # max range. max_ranges = [] for i, value in enumerate(values): self.data[i].appendleft(value) max_ranges.append(max([max(self.data[i]), abs(min(self.data[i]))])) if len(self.data[i]) > self.max_x: self.data[i].pop() # Re-scale y-axis. max_y_range = max(max_ranges) y_range = bisect.bisect_left(self.y_ranges, max_y_range) if y_range < len(self.y_ranges): self.max_y = self.y_ranges[y_range] elif max_y_range > self.max_y: self.max_y += self.max_y elif max_y_range < self.max_y / 2: self.max_y = self.max_y / 2 self.axis_y.setRange(-self.max_y, self.max_y) # Ensure floats are used to label y axis if the range is small. if self.max_y <= 5: self.axis_y.setLabelFormat("%2.2f") else: self.axis_y.setLabelFormat("%d") # Update the line series with the data. for i, line_series in enumerate(self.series): line_series.clear() xy_vals = [] for j in range(self.max_x): val = self.data[i][self.max_x - 1 - j] xy_vals.append((j, val)) for point in xy_vals: line_series.append(*point) def set_theme(self, theme): """ Sets the theme / look for the plotter pane. """ if theme == 'day': self.chart.setTheme(QChart.ChartThemeLight) elif theme == 'night': self.chart.setTheme(QChart.ChartThemeDark) else: self.chart.setTheme(QChart.ChartThemeHighContrast)
class widgetIDChart(QtWidgets.QWidget, qdesignFDChart.Ui_Form): actionRequestFromTable = pyqtSignal() def __init__(self, parent=None): super(widgetIDChart, self).__init__(parent) self.setupUi(self) tribchartmenustyle(self) # Default Settings self.nbins = 10 # Extra Menu Items # Distribution Type Selection self.comboBoxDistr = XComboBoxDict(self) self.comboBoxDistr.setMinimumSize(QtCore.QSize(100, 30)) self.knowndistr = distr._distrnames() self.comboBoxDistr.addItem({'None': 'None'}) self.comboBoxDistr.addItems(self.knowndistr) self.comboBoxDistr.setObjectName("comboBoxDistr") self.horizontalLayout.insertWidget(3, self.comboBoxDistr) self.comboBoxDistr.currentTextChanged.connect( self.onComboBoxDistrChanged) self.activeDist = str( self.distrfromname(self.comboBoxDistr.currentText())) # Spinbox Label self.spinBoxLab = QtWidgets.QLabel(self) self.spinBoxLab.setText(' Bins: ') self.horizontalLayout.insertWidget(4, self.spinBoxLab) # Histogram N columns quick select spin box self.spinBoxDistr = QtWidgets.QSpinBox(self) self.spinBoxDistr.setMinimumSize(QtCore.QSize(50, 30)) self.spinBoxDistr.setRange(2, 100) self.spinBoxDistr.setSingleStep(1) self.spinBoxDistr.setValue(self.nbins) self.spinBoxDistr.setObjectName("spinBoxDistr") self.horizontalLayout.insertWidget(5, self.spinBoxDistr) self.spinBoxDistr.valueChanged.connect(self.onSpinBoxChanged) # SetUp for Chart to Display Distribution curves and inputs. self.chart = QChart() self.chart.legend().setVisible(False) # SetUp Visible Chart Axes self.chart.axisX = QValueAxis() self.chart.axisY = QValueAxis() self.chart.axisX.setTickCount(10) self.chart.axisY.setTickCount(2) self.chart.axisX.setTitleText("Value") self.chart.axisY.setTitleText("Probability") # HistogramAxis self.chart.axisHist = QValueAxis() self.chart.addAxis(self.chart.axisHist, Qt.AlignRight) self.chart.axisHist.setTickCount(10) self.chart.axisHist.setTitleText("Count") self.chart.axisBins = QValueAxis() self.chart.addAxis(self.chart.axisBins, Qt.AlignBottom) self.chart.axisBins.setVisible(False) # Add Chart to Chartview and Chartview to Widget self.chartview = QChartView(self.chart) self.verticalLayout.addWidget(self.chartview) self.chartview.setRenderHint(QPainter.Antialiasing) def axesMinMax(self): # returns a length 4 list of the axes min and max values [x1,x2,y1,y2] return [ self.chart.axisX.min(), self.chart.axisX.max(), self.chart.axisY.min(), self.chart.axisY.max() ] def setAxes(self, series): # assigns a series to the chart default axes self.chart.setAxisX(self.chart.axisX, series) self.chart.setAxisY(self.chart.axisY, series) def setAxesMinMax(self, x1, x2, y1, y2): # sets the min max values in X and Y self.chart.axisX.setMin(x1) self.chart.axisX.setMax(x2) self.chart.axisY.setMin(y1) self.chart.axisY.setMax(y2) def setTitle(self, title): self.chart.setTitle(title) def setRawData(self, datar): self.datar = datar self.ndata = len(self.datar) def addDistrLine(self, n, kstats): self.kstats = kstats data = distr.distrpdf(self.activeDist, n, **kstats) xmin = min(data['X']) xmax = max(data['X']) ymin = min(data['Y']) ymax = max(data['Y']) xscal = 0.1 * (xmax - xmin) yscal = 0.1 * (ymax - 0) self.chart.axisY.setRange(0, ymax + yscal) self.lineDist = XLineSeries(data, xkey='X')[0] def addHistLine(self): # fits a distribution to the histogram and draws a line self.kstats = distr.distrfit(self.activeDist, self.datar) #hist[0]) self.addDistrLine(1000, self.kstats) def addHistogram(self, nbins): # accepts a data array datar and int nbins to calculate a histogram self.nbins = nbins self.spinBoxDistr.setValue(nbins) try: self.hist = np.histogram(self.datar, bins=nbins) except: pass # create a function which displays a msg in the middle of the chart by raising a data error of sorts self.histseries = XDictSet({'Hist': self.hist[0]}) # return barsets self.histsets = self.histseries.findChildren(QBarSet) def updateChart(self): self.chart.removeAllSeries() hmin = self.hist[1][0] hmax = self.hist[1][-1] cmax = max(self.hist[0]) + 1 binwidth = self.hist[1][1] - self.hist[1][0] binscale = 1 / binwidth if self.activeDist == 'None': self.chart.axisY.setVisible(False) self.chart.axisX.setMin(hmin) self.chart.axisX.setMax(hmax) else: self.chart.axisY.setVisible(True) try: # Add histogram bars self.chart.axisBins.setRange(-1, self.nbins) self.chart.axisHist.setRange(0, cmax) self.chart.addSeries(self.histseries) self.histseries.setBarWidth(1) # Set Colours self.histsets[0].setColor(tribColours.tribBlue) # Update Axes self.chart.setAxisX(self.chart.axisBins, self.histseries) self.chart.setAxisY(self.chart.axisHist, self.histseries) except NameError: pass if self.activeDist != 'None': try: # Add distribution line self.addHistLine() # Fit Line Formatting linepen = QPen() linepen.setWidth(2.5) linepen.setColor(tribColours.tribCoral) self.lineDist.setPen(linepen) self.chart.addSeries(self.lineDist) self.setAxes(self.lineDist) self.chart.axisX.setRange(hmin - binwidth, hmax + binwidth) except AttributeError: pass @pyqtSlot(int) def onSpinBoxChanged(self, n): self.nbins = n self.addHistogram(self.nbins) self.updateChart() def distrfromname(self, search): for key, name in self.knowndistr.items(): if name == search: return key @pyqtSlot(str) def onComboBoxDistrChanged(self, name): self.activeDist = self.comboBoxDistr.currentKey() self.addHistogram(self.nbins) self.updateChart() @pyqtSlot(dict) def receiveFromTable(self, datadict): self.setRawData(datadict['Value']) self.addHistogram(self.nbins) self.updateChart()
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.pixelRow = QLabel() self.pixelRow.setFixedSize(20, nbPixels) self.matrixDisplay = QTextEdit() self.matrixDisplay.setFixedSize(165, 180) self.valuesDisplay = QTextEdit() self.valuesDisplay.setFixedSize(50, nbPixels) # CHART self.serie = QLineSeries() self.chart = QChart() self.chart.addSeries(self.serie) self.chart.legend().hide() self.chart.layout().setContentsMargins(0, 0, 0, 0) self.xAxis = QValueAxis() self.xAxis.setTitleText("Height") self.xAxis.setLabelFormat("%d") self.xAxis.setRange(0, nbPixels) self.yAxis = QValueAxis() self.yAxis.setLabelFormat("%d") self.yAxis.setTitleText("Distance ") self.yAxis.setRange(0, couleurMax) self.chart.setAxisX(self.xAxis) self.chart.setAxisY(self.yAxis) self.serie.attachAxis(self.xAxis) self.serie.attachAxis(self.yAxis) self.chart.setTitle("Profile line") self.chartView = QChartView(self) self.chartView.setChart(self.chart) self.chartView.rotate(90) self.chartView.setGeometry(20, 195, 450, 400) #end CHART self.timer = QTimer(self) self.timer.setInterval(periode) self.timer.start self.timer.timeout.connect(self.refresh) self.startButton = QPushButton("START") self.startButton.setDefault(True) self.startButton.clicked.connect(self.startTimer) self.startButton.setFixedSize(100, 50) self.stopButton = QPushButton("STOP") self.stopButton.setDefault(True) self.stopButton.clicked.connect(self.stopTimer) self.stopButton.setFixedSize(100, 50) topLayout = QGridLayout() topLayout.addWidget(self.startButton, 0, 0) topLayout.addWidget(self.stopButton, 1, 0) topLayout.addWidget(self.matrixDisplay, 0, 1, 2, 1) mainLayout = QGridLayout() mainLayout.addLayout(topLayout, 0, 0) mainLayout.addWidget(self.pixelRow, 0, 1, 2, 1) mainLayout.addWidget(self.valuesDisplay, 0, 2, 2, 1) mainLayout.addWidget(self.chartView, 1, 0) mainwidget = QWidget() mainwidget.setLayout(mainLayout) self.setCentralWidget(mainwidget) self.setWindowTitle("Minecraft Depth Map") def startTimer(self): self.timer.start() def stopTimer(self): self.timer.stop() def refresh(self): self.serie.clear() self.valuesDisplay.clear() screen = app.primaryScreen() #grabWindow(wID, x, y, w, h) pix = QPixmap( screen.grabWindow(0, int((screenW * 3) / 4) - 10, int((screenH - nbPixels) / 2), 20, nbPixels)) self.pixelRow.setPixmap(pix) img = QImage(pix.toImage()) array = [0 for i in range(nbBlocsH)] for i in range(nbBlocsH): y = nbPixels - (i * (nbPixels / nbBlocsH) + (nbPixels / (2 * nbBlocsH))) colorvalue = 255 - QColor(img.pixel(10, y)).black() self.valuesDisplay.append(str(colorvalue)) self.valuesDisplay.append("\n") self.serie.append(y, colorvalue) #convert colors from 0->couleurMax to 0->nbBlocsD if colorvalue > couleurMax: colorvalue = nbBlocsD elif colorvalue < couleurMin: colorvalue = 0 else: colorvalue = int(colorvalue / (couleurMax / nbBlocsD)) array[i] = colorvalue self.convertToMatrix(array) def convertToMatrix(self, array): matrix = [[0 for j in range(nbBlocsD)] for i in range(nbBlocsH)] for i in range(nbBlocsH): if array[i] < nbBlocsD: matrix[i][array[i]] = 1 if i < (nbBlocsH - 1): if array[i + 1] > (array[i] + 1): for j in range(array[i] + 1, min(nbBlocsD, array[i + 1])): matrix[i][j] = 1 if i > 0: if array[i - 1] > (array[i] + 1): for j in range(array[i] + 1, min(nbBlocsD, array[i - 1])): matrix[i][j] = 1 #test1Scalable.convertToHexa(motifF,nbBlocsD,nbBlocsH) # es-ce bien la fonction a utiliser pour l'affichage ? self.displayMatrix(matrix) def displayMatrix(self, matrix): self.matrixDisplay.clear() for j in range(nbBlocsD - 1, -1, -1): line = "" for i in range(nbBlocsH): line = line + str(matrix[i][j]) + " " #self.matrixDisplay.append( line) #self.matrixDisplay.append( str(matrix[i][j]) + " ") #self.matrixDisplay.moveCursor( QTextCursor.EndOfLine ) self.matrixDisplay.append(line)
class MicroPythonGraphWidget(QWidget): """ Class implementing the MicroPython graph widget. @signal dataFlood emitted to indicate, that too much data is received """ dataFlood = pyqtSignal() def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(MicroPythonGraphWidget, self).__init__(parent) self.__layout = QHBoxLayout() self.__layout.setContentsMargins(2, 2, 2, 2) self.setLayout(self.__layout) self.__chartView = QChartView(self) self.__chartView.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.__layout.addWidget(self.__chartView) self.__verticalLayout = QVBoxLayout() self.__verticalLayout.setContentsMargins(0, 0, 0, 0) self.__layout.addLayout(self.__verticalLayout) self.__saveButton = QToolButton(self) self.__saveButton.setIcon(UI.PixmapCache.getIcon("fileSave")) self.__saveButton.setToolTip(self.tr("Press to save the raw data")) self.__saveButton.clicked.connect(self.on_saveButton_clicked) self.__verticalLayout.addWidget(self.__saveButton) self.__verticalLayout.setAlignment(self.__saveButton, Qt.AlignHCenter) spacerItem = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) self.__verticalLayout.addItem(spacerItem) label = QLabel(self.tr("max. X:")) self.__verticalLayout.addWidget(label) self.__verticalLayout.setAlignment(label, Qt.AlignHCenter) self.__maxX = 100 self.__maxXSpinBox = QSpinBox() self.__maxXSpinBox.setMinimum(100) self.__maxXSpinBox.setMaximum(1000) self.__maxXSpinBox.setSingleStep(100) self.__maxXSpinBox.setToolTip( self.tr("Enter the maximum number of data points to be plotted.")) self.__maxXSpinBox.setValue(self.__maxX) self.__maxXSpinBox.setAlignment(Qt.AlignRight) self.__verticalLayout.addWidget(self.__maxXSpinBox) # holds the data to be checked for plotable data self.__inputBuffer = [] # holds the raw data self.__rawData = [] self.__dirty = False self.__maxY = 1000 self.__flooded = False # flag indicating a data flood self.__data = [deque([0] * self.__maxX)] self.__series = [QLineSeries()] # Y-axis ranges self.__yRanges = [1, 5, 10, 25, 50, 100, 250, 500, 1000] # setup the chart self.__chart = QChart() self.__chart.legend().hide() self.__chart.addSeries(self.__series[0]) self.__axisX = QValueAxis() self.__axisX.setRange(0, self.__maxX) self.__axisX.setLabelFormat("time") self.__axisY = QValueAxis() self.__axisY.setRange(-self.__maxY, self.__maxY) self.__axisY.setLabelFormat("%d") self.__chart.setAxisX(self.__axisX, self.__series[0]) self.__chart.setAxisY(self.__axisY, self.__series[0]) self.__chartView.setChart(self.__chart) self.__chartView.setRenderHint(QPainter.Antialiasing) self.__maxXSpinBox.valueChanged.connect(self.__handleMaxXChanged) @pyqtSlot(bytes) def processData(self, data): """ Public slot to process the raw data. It takes raw bytes, checks the data for a valid tuple of ints or floats and adds the data to the graph. If the the length of the bytes data is greater than 1024 then a dataFlood signal is emitted to ensure eric can take action to remain responsive. @param data raw data received from the connected device via the main device widget @type bytes """ # flooding guard if self.__flooded: return if len(data) > 1024: self.__flooded = True self.dataFlood.emit() return # disable the inputs while processing data self.__saveButton.setEnabled(False) self.__maxXSpinBox.setEnabled(False) data = data.replace(b"\r\n", b"\n").replace(b"\r", b"\n") self.__inputBuffer.append(data) # check if the data contains a Python tuple containing numbers (int # or float) on a single line inputBytes = b"".join(self.__inputBuffer) lines = inputBytes.splitlines(True) for line in lines: if not line.endswith(b"\n"): # incomplete line (last line); skip it break line = line.strip() if line.startswith(b"(") and line.endswith(b")"): # it may be a tuple we are interested in rawValues = [val.strip() for val in line[1:-1].split(b",")] values = [] for raw in rawValues: try: values.append(int(raw)) # ok, it is an integer continue except ValueError: # test for a float pass try: values.append(float(raw)) except ValueError: # it is not an int or float, ignore it continue if values: self.__addData(tuple(values)) self.__inputBuffer = [] if lines[-1] and not lines[-1].endswith(b"\n"): # Append any left over bytes for processing next time data is # received. self.__inputBuffer.append(lines[-1]) # re-enable the inputs self.__saveButton.setEnabled(True) self.__maxXSpinBox.setEnabled(True) def __addData(self, values): """ Private method to add a tuple of values to the graph. It ensures there are the required number of line series, adds the data to the line series and updates the range of the chart so the chart displays nicely. @param values tuple containing the data to be added @type tuple of int or float """ # store incoming data to be able to dump it as CSV upon request self.__rawData.append(values) self.__dirty = True # check number of incoming values and adjust line series accordingly if len(values) != len(self.__series): valuesLen = len(values) seriesLen = len(self.__series) if valuesLen > seriesLen: # add a nwe line series for _index in range(valuesLen - seriesLen): newSeries = QLineSeries() self.__chart.addSeries(newSeries) self.__chart.setAxisX(self.__axisX, newSeries) self.__chart.setAxisY(self.__axisY, newSeries) self.__series.append(newSeries) self.__data.append(deque([0] * self.__maxX)) else: # remove obsolete line series for oldSeries in self.__series[valuesLen:]: self.__chart.removeSeries(oldSeries) self.__series = self.__series[:valuesLen] self.__data = self.__data[:valuesLen] # add the new values to the display and compute the maximum range maxRanges = [] for index, value in enumerate(values): self.__data[index].appendleft(value) maxRanges.append( max([max(self.__data[index]), abs(min(self.__data[index]))])) if len(self.__data[index]) > self.__maxX: self.__data[index].pop() # re-scale the y-axis maxYRange = max(maxRanges) yRange = bisect.bisect_left(self.__yRanges, maxYRange) if yRange < len(self.__yRanges): self.__maxY = self.__yRanges[yRange] elif maxYRange > self.__maxY: self.__maxY += self.__maxY elif maxYRange < self.__maxY / 2: self.__maxY /= 2 self.__axisY.setRange(-self.__maxY, self.__maxY) # ensure that floats are used to label the y-axis if the range is small if self.__maxY <= 5: self.__axisY.setLabelFormat("%2.2f") else: self.__axisY.setLabelFormat("%d") # update the line series for index, series in enumerate(self.__series): series.clear() xyValues = [] for x in range(self.__maxX): value = self.__data[index][self.__maxX - 1 - x] xyValues.append((x, value)) for xy in xyValues: series.append(*xy) @pyqtSlot() def on_saveButton_clicked(self): """ Private slot to save the raw data to a CSV file. """ self.saveData() def hasData(self): """ Public method to check, if the chart contains some valid data. @return flag indicating valid data @rtype bool """ return len(self.__rawData) > 0 def isDirty(self): """ Public method to check, if the chart contains unsaved data. @return flag indicating unsaved data @rtype bool """ return self.hasData() and self.__dirty def saveData(self): """ Public method to save the dialog's raw data. @return flag indicating success @rtype bool """ baseDir = (Preferences.getMultiProject("Workspace") or os.path.expanduser("~")) dataDir = os.path.join(baseDir, "data_capture") if not os.path.exists(dataDir): os.makedirs(dataDir) # save the raw data as a CSV file fileName = "{0}.csv".format(time.strftime("%Y%m%d-%H%M%S")) fullPath = os.path.join(dataDir, fileName) try: csvFile = open(fullPath, "w") csvWriter = csv.writer(csvFile) csvWriter.writerows(self.__rawData) csvFile.close() self.__dirty = False return True except (IOError, OSError) as err: E5MessageBox.critical( self, self.tr("Save Chart Data"), self.tr("""<p>The chart data could not be saved into file""" """ <b>{0}</b>.</p><p>Reason: {1}</p>""").format( fullPath, str(err))) return False @pyqtSlot(int) def __handleMaxXChanged(self, value): """ Private slot handling a change of the max. X spin box. @param value value of the spin box @type int """ delta = value - self.__maxX if delta == 0: # nothing to change return elif delta > 0: # range must be increased for deq in self.__data: deq.extend([0] * delta) else: # range must be decreased data = [] for deq in self.__data: data.append(deque(list(deq)[:value])) self.__data = data self.__maxX = value self.__axisX.setRange(0, self.__maxX)
def createBar(self): print("in create bar") min_num, max_num = 0, 100 max_count = 0 linked_bag_list = [] try: df = self.linked['Beam Diff'].dropna() linked_bag_list = df.values.tolist() min_num = int(min(linked_bag_list)) if min_num > 0: # check if greater than 0, set to 0 min_num = 0 max_num = int(max(linked_bag_list)) except AttributeError: self.statusbar.showMessage('Data not ready') count = linked_bag_list count = [0] * (max_num + 1) # choose the largest num as length of count for num in linked_bag_list: count[int(num)] += 1 # update every number's count max_count = max(count) setBar = QBarSet('Beam Difference Occurrence') setBar.append(count) brush = QBrush(QColor(0x57B1FD)) pen = QPen(QColor(0x57B1FD)) pen.setWidth(2) setBar.setPen(pen) setBar.setBrush(brush) series = QBarSeries() series.append(setBar) chart = QChart() font = QFont() font.setPixelSize(18) chart.setTitleFont(font) chart.setTitle('Linked Bags Histogram') chart.addSeries(series) chart.setAnimationOptions(QChart.SeriesAnimations) axisX = QValueAxis() axisX.setRange(min_num, max_num+20) chart.setAxisX(axisX, series) axisY = QValueAxis() axisY.setRange(0, max_count+20) chart.setAxisY(axisY, series) chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) chartView = QChartView(chart) chartView.setRenderHint(QPainter.Antialiasing) # MainWindow.setCentralWidget(chartView) return chartView
class ChartView(QChartView): def __init__(self, file, parent=None): super(ChartView, self).__init__(parent) self._chart = QChart() self._chart.setAcceptHoverEvents(True) self.setChart(self._chart) self.initUi(file) def initUi(self, file): if isinstance(file, dict): return self.__analysis(file) if isinstance(file, str): if not os.path.isfile(file): return self.__analysis(json.loads(file)) with open(file, "rb") as fp: data = fp.read() encoding = chardet.detect(data) or {} data = data.decode(encoding.get("encoding") or "utf-8") self.__analysis(json.loads(data)) # def onSeriesHoverd(self, point, state): # print(point, state) def mouseMoveEvent(self, event): super(ChartView, self).mouseMoveEvent(event) # 获取x和y轴的最小最大值 axisX, axisY = self._chart.axisX(), self._chart.axisY() min_x, max_x = axisX.min(), axisX.max() min_y, max_y = axisY.min(), axisY.max() # 把鼠标位置所在点转换为对应的xy值 x = self._chart.mapToValue(event.pos()).x() y = self._chart.mapToValue(event.pos()).y() index = round(x) # 四舍五入 print(x, y, index) # 得到在坐标系中的所有series的类型和点 points = [(s.type(), s.at(index)) for s in self._chart.series() if min_x <= x <= max_x and min_y <= y <= max_y] print(points) def __getColor(self, color=None, default=Qt.white): ''' :param color: int|str|[r,g,b]|[r,g,b,a] ''' if not color: return QColor(default) if isinstance(color, QBrush): return color # 比如[r,g,b]或[r,g,b,a] if isinstance(color, list) and 3 <= len(color) <= 4: return QColor(*color) else: return QColor(color) def __getPen(self, pen=None, default=QPen(Qt.white, 1, Qt.SolidLine, Qt.SquareCap, Qt.BevelJoin)): ''' :param pen: pen json ''' if not pen or not isinstance(pen, dict): return default return QPen(self.__getColor(pen.get("color", None) or default.color()), pen.get("width", 1) or 1, pen.get("style", 0) or 0, pen.get("capStyle", 16) or 16, pen.get("joinStyle", 64) or 64) def __getAlignment(self, alignment): ''' :param alignment: left|top|right|bottom ''' try: return getattr(Qt, "Align" + alignment.capitalize()) except: return Qt.AlignTop # if alignment == "left": # return Qt.AlignLeft # if alignment == "right": # return Qt.AlignRight # if alignment == "bottom": # return Qt.AlignBottom # return Qt.AlignTop def __setTitle(self, title=None): ''' :param title: title json ''' if not title or not isinstance(title, dict): return # 设置标题 self._chart.setTitle(title.get("text", "") or "") # 设置标题颜色 self._chart.setTitleBrush( self.__getColor( title.get("color", self._chart.titleBrush()) or self._chart.titleBrush())) # 设置标题字体 font = QFont(title.get("font", "") or self._chart.titleFont()) pointSize = title.get("pointSize", -1) or -1 if pointSize > 0: font.setPointSize(pointSize) font.setWeight(title.get("weight", -1) or -1) font.setItalic(title.get("italic", False) or False) self._chart.setTitleFont(font) def __setAnimation(self, animation=None): ''' :param value: animation json ''' if not animation or not isinstance(animation, dict): return # 动画持续时间 self._chart.setAnimationDuration( animation.get("duration", 1000) or 1000) # 设置动画曲线 self._chart.setAnimationEasingCurve( EasingCurve.get(animation.get("curve", 10) or 10, None) or QEasingCurve.OutQuart) # 设置开启何种动画 self._chart.setAnimationOptions( AnimationOptions.get(animation.get("options", 0) or 0, None) or QChart.NoAnimation) def __setBackground(self, background=None): ''' :param background:background json ''' if not background or not isinstance(background, dict): return # 设置是否背景可用 self._chart.setBackgroundVisible( background.get("visible", True) or True) # 设置背景矩形的圆角 self._chart.setBackgroundRoundness(background.get("radius", 0) or 0) # 设置下拉阴影 self._chart.setDropShadowEnabled( background.get("dropShadow", True) or True) # 设置pen self._chart.setBackgroundPen( self.__getPen(background.get("pen", None), self._chart.backgroundPen())) # 设置背景 image = background.get("image", None) color = background.get("color", None) if image: self._chart.setBackgroundBrush(QBrush(QPixmap(image))) elif color: self._chart.setBackgroundBrush( self.__getColor(color, self._chart.backgroundBrush())) def __setMargins(self, margins=None): ''' :param margins: margins json ''' if not margins or not isinstance(margins, dict): return left = margins.get("left", 20) or 20 top = margins.get("top", 20) or 20 right = margins.get("right", 20) or 20 bottom = margins.get("bottom", 20) or 20 self._chart.setMargins(QMargins(left, top, right, bottom)) def __setLegend(self, legend=None): ''' :param legend: legend json ''' if not legend or not isinstance(legend, dict): return _legend = self._chart.legend() _legend.setAlignment(self.__getAlignment(legend.get("alignment", None))) _legend.setShowToolTips(legend.get("showToolTips", True) or True) def __getSerie(self, serie=None): if not serie or not isinstance(serie, dict): return None types = serie.get("type", "") or "" data = serie.get("data", []) or [] if not data or not isinstance(data, list): return None if types == "line": _series = QLineSeries(self._chart) else: return None # 设置series名字 _series.setName(serie.get("name", "") or "") # 添加数据到series中 for index, value in enumerate(data): # 保证vlaue必须是数字 _series.append(index, value if type(value) in (int, float) else 0) return _series def __setSeries(self, series=None): if not series or not isinstance(series, list): return for serie in series: _serie = self.__getSerie(serie) if _serie: # _serie.hovered.connect(self.onSeriesHoverd) self._chart.addSeries(_serie) # 创建默认的xy轴 self._chart.createDefaultAxes() def __setAxisX(self, axisx=None): if not axisx or not isinstance(axisx, dict): return series = self._chart.series() if not series: return types = axisx.get("type", None) data = axisx.get("data", []) or [] if not data or not isinstance(data, list): return None minx = self._chart.axisX().min() maxx = self._chart.axisX().max() if types == "category": xaxis = QCategoryAxis( self._chart, labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue) # 隐藏网格 xaxis.setGridLineVisible(False) # 刻度条数 tickc_d = len(data) tickc = tickc_d if tickc_d > 1 else self._chart.axisX().tickCount() xaxis.setTickCount(tickc) # 强制x轴刻度与新刻度条数一致 self._chart.axisX().setTickCount(tickc) step = (maxx - minx) / (tickc - 1) for i in range(min(tickc_d, tickc)): xaxis.append(data[i], minx + i * step) self._chart.setAxisX(xaxis, series[-1]) def __analysis(self, datas): ''' analysis json data :param datas: json data ''' # 标题 self.__setTitle(datas.get("title", None)) # 抗锯齿 if (datas.get("antialiasing", False) or False): self.setRenderHint(QPainter.Antialiasing) # 主题 self._chart.setTheme(datas.get("theme", 0) or 0) # 动画 self.__setAnimation(datas.get("animation", None)) # 背景设置 self.__setBackground(datas.get("background", None)) # 边距设置 self.__setMargins(datas.get("margins", None)) # 设置图例 self.__setLegend(datas.get("legend", None)) # 设置series self.__setSeries(datas.get("series", None)) # 自定义的x轴 self.__setAxisX(datas.get("axisx", None))
class ContentView(QWidget): def __init__(self): super().__init__() self.m_chart_1 = QChart() self.m_chart_2 = QChart() self.m_chart_3 = QChart() self.m_chart_4 = QChart() self.m_series_1 = QLineSeries() self.m_series_2 = QLineSeries() self.m_series_3 = QLineSeries() self.m_series_4 = QLineSeries() self.y_original = [] self.x_data = [] self.y_processed = [] self.sampling_rate = 0 self.pathForVocalMute = "" self.select_action_drop = QComboBox() self.echo_shift = 0.4 self.echo_alpha = 0.5 self.init_ui() def init_ui(self): main_layout = QVBoxLayout() # Drag&Drop area drag_drop = DragDropArea(parent=self) main_layout.addWidget(drag_drop) # Chart layout chart_layout_1 = QHBoxLayout() chart_layout_2 = QHBoxLayout() # Chart 1 chart_view_1 = QChartView(self.m_chart_1) chart_view_1.setMinimumSize(400, 300) self.m_chart_1.addSeries(self.m_series_1) pen = self.m_series_1.pen() pen.setColor(Qt.red) pen.setWidthF(.1) self.m_series_1.setPen(pen) self.m_series_1.setUseOpenGL(True) axis_x = QValueAxis() axis_x.setRange(0, 100) axis_x.setLabelFormat("%g") axis_x.setTitleText("Samples") axis_y = QValueAxis() axis_y.setRange(-10, 10) axis_y.setTitleText("Audio level") self.m_chart_1.setAxisX(axis_x, self.m_series_1) self.m_chart_1.setAxisY(axis_y, self.m_series_1) self.m_chart_1.setTitle("Original signal time domain") chart_layout_1.addWidget(chart_view_1) # Chart 2 chart_view_2 = QChartView(self.m_chart_2) chart_view_2.setMinimumSize(400, 300) self.m_chart_2.setTitle("Original signal frequency domain") pen = self.m_series_2.pen() pen.setColor(Qt.blue) pen.setWidthF(.1) self.m_series_2.setPen(pen) self.m_series_2.setUseOpenGL(True) self.m_chart_2.addSeries(self.m_series_2) chart_layout_1.addWidget(chart_view_2) # Chart 3 chart_view_3 = QChartView(self.m_chart_3) chart_view_3.setMinimumSize(400, 300) self.m_chart_3.addSeries(self.m_series_3) pen = self.m_series_3.pen() pen.setColor(Qt.green) pen.setWidthF(.1) self.m_series_3.setPen(pen) self.m_series_3.setUseOpenGL(True) axis_x = QValueAxis() axis_x.setRange(0, 100) axis_x.setLabelFormat("%g") axis_x.setTitleText("Samples") axis_y = QValueAxis() axis_y.setRange(-10, 10) axis_y.setTitleText("Audio level") self.m_chart_3.setAxisX(axis_x, self.m_series_3) self.m_chart_3.setAxisY(axis_y, self.m_series_3) self.m_chart_3.setTitle("Processed signal time domain") chart_layout_2.addWidget(chart_view_3) # Chart 4 chart_view_4 = QChartView(self.m_chart_4) chart_view_4.setMinimumSize(400, 300) self.m_chart_4.setTitle("Processed signal frequency domain") pen = self.m_series_4.pen() pen.setColor(Qt.magenta) pen.setWidthF(.1) self.m_series_4.setPen(pen) self.m_series_4.setUseOpenGL(True) self.m_chart_4.addSeries(self.m_series_4) chart_layout_2.addWidget(chart_view_4) main_layout.addLayout(chart_layout_1) main_layout.addLayout(chart_layout_2) # Action buttons player_layout = QHBoxLayout() self.select_action_drop.addItems([ "Add noise", "Filter", "Mute equipment", "Mute vocal", "Add echo", "Filter echo" ]) player_layout.addWidget(self.select_action_drop) noise_jc = QIcon('rate_ic.png') noise_btn = QPushButton('Process') noise_btn.setIcon(noise_jc) noise_btn.clicked.connect(self.on_action) player_layout.addWidget(noise_btn) play_jc = QIcon('play_ic.png') play_orig_btn = QPushButton('Play Original') play_orig_btn.setIcon(play_jc) play_orig_btn.clicked.connect(self.on_play_orig) player_layout.addWidget(play_orig_btn) play_jc = QIcon('play_ic.png') play_btn = QPushButton('Play Processed') play_btn.setIcon(play_jc) play_btn.clicked.connect(self.on_play) player_layout.addWidget(play_btn) stop_jc = QIcon('stop_ic.png') stop_btn = QPushButton('Stop') stop_btn.setIcon(stop_jc) stop_btn.clicked.connect(self.on_stop) player_layout.addWidget(stop_btn) main_layout.addLayout(player_layout) self.setLayout(main_layout) '''' Toolbar actions ''' def browse_file(self): path1 = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'), '*.wav') print(path1[0]) rate, data = wavfile.read(path1[0]) self.sampling_rate = rate self.y_original = data[:, 0] self.show_original_data() def on_file_upload(self, file_url): print(file_url[7:]) self.pathForVocalMute = file_url[7:] rate, data = wavfile.read(file_url[7:]) self.sampling_rate = rate self.y_original = data[:, 0] self.show_original_data() def on_save(self): print("on_save") if len(self.y_processed) > 0: path = QFileDialog.getSaveFileName(self, 'Save File', os.getenv('HOME'), 'audio/wav') if path[0] != '': data2 = np.asarray([self.y_processed, self.y_processed]).transpose() wavfile.write(path[0], self.sampling_rate, data2) else: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("No path") msg.setInformativeText("You should define path to save file") msg.setWindowTitle("Error") msg.exec_() else: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("No data") msg.setInformativeText( "No data to save, you should upload and process sound file") msg.setWindowTitle("Error") msg.exec_() '''' Action selection ''' def on_action(self): if self.select_action_drop.currentText() == "Add noise": self.on_add_noise() elif self.select_action_drop.currentText() == "Filter": self.on_filter() elif self.select_action_drop.currentText() == "Mute equipment": self.on_mute_equipment() elif self.select_action_drop.currentText() == "Mute vocal": self.on_mute_voice() elif self.select_action_drop.currentText() == "Add echo": self.on_add_echo() elif self.select_action_drop.currentText() == "Filter echo": self.on_filter_echo() ''' Noise addition ''' def on_add_noise(self): if len(self.y_original) == 0: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Upload sound file") msg.setInformativeText( "First you should add sound file to process") msg.setWindowTitle("Error") msg.exec_() return noise = np.random.normal(0, self.y_original.max() / 30, len(self.y_original)) arr1 = np.array(self.y_original) self.y_processed = arr1 + noise self.show_processed_data() def on_filter(self): print("on_filter") if len(self.y_original) == 0: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Upload sound file") msg.setInformativeText( "First you should add sound file to process") msg.setWindowTitle("Error") msg.exec_() return filter1, filter2, limit1, limit2, extra, max_ripple, min_attenuation, ok = FilterSelectionDialog.show_dialog( parent=self) print(filter1, filter2, limit1, limit2, extra, max_ripple, min_attenuation, ok) if ok: if filter1 == "FIR filter": self.on_fir_filter(filter2, limit1, limit2, extra) elif filter1 == "IIR filter": self.on_iir_filter(filter2, limit1, limit2, extra, max_ripple, min_attenuation) def on_mute_equipment(self): print("on_mute_equipment") check_piano, check_organ, check_flute, check_french_horn, check_trumpet, check_violin, \ check_guitar_acoustic, check_guitar_bass, check_clarinet, \ check_saxophone, ok = MuteInstrumentsDialog.show_dialog(parent=self) print(check_piano, check_organ, check_flute, check_french_horn, check_trumpet, check_violin, check_guitar_acoustic, check_guitar_bass, check_clarinet, check_saxophone, ok) ''' Piano A0 (28 Hz) to C8 (4,186 Hz or 4.1 KHz) Organ C0 (16 Hz) to A9 (7,040 KHz) Concert Flute C4 (262 Hz) to B6 (1,976 Hz) French Horn A2 (110 Hz) to A5 (880 Hz) Trumpet E3 (165 Hz) to B5 (988 Hz) Violin G3 (196 Hz) - G7 (3,136 Hz) (G-D-E-A) (or C8 (4,186 Hz?) Guitar (Acoustic) E2 (82 Hz) to F6 (1,397 Hz) Guitar (Bass) 4 string E1 (41 Hz) to C4 (262 Hz) Clarinet E3 (165 Hz) to G6 (1,568 Hz) Saxaphone Eb 138-830 (880) ''' if ok: print(check_piano) limit1 = 0.1 limit2 = 0.2 if check_piano: pass elif check_organ: pass elif check_flute: limit1 = 262 / self.sampling_rate limit2 = 1976 / self.sampling_rate pass elif check_french_horn: limit1 = 110 / self.sampling_rate limit2 = 880 / self.sampling_rate pass elif check_trumpet: limit1 = 165 / self.sampling_rate limit2 = 988 / self.sampling_rate pass elif check_violin: limit1 = 196 / self.sampling_rate limit2 = 3136 / self.sampling_rate pass elif check_guitar_acoustic: limit1 = 82 / self.sampling_rate limit2 = 1397 / self.sampling_rate pass elif check_guitar_bass: limit1 = 41 / self.sampling_rate limit2 = 262 / self.sampling_rate pass elif check_clarinet: limit1 = 165 / self.sampling_rate limit2 = 1568 / self.sampling_rate pass elif check_saxophone: limit1 = 138 / self.sampling_rate limit2 = 880 / self.sampling_rate pass print(limit1, limit2) print([ 0.0, 0.0001, limit1 - 0.0001, limit1, limit2, limit2 + 0.0001, 0.9991, 1.0 ], [0, 1, 1, 0, 0, 1, 1, 0]) design_filter = signal.firwin2(1000000, [ 0.0, 0.0001, limit1 - 0.0001, limit1, limit2, limit2 + 0.0001, 0.9991, 1.0 ], [0, 1, 1, 0, 0, 1, 1, 0]) self.y_processed = signal.convolve(self.y_original, design_filter, mode='same') w1, h1 = signal.freqz(design_filter) result = FilterResponseDialog.show_dialog(parent=self, w1=w1, h1=h1) if result: self.show_processed_data() def on_mute_voice(self): y, sr = librosa.load(self.pathForVocalMute, sr=self.sampling_rate) S_full, phase = librosa.magphase(librosa.stft(y)) S_filter = librosa.decompose.nn_filter( S_full, aggregate=np.median, metric='cosine', width=int(librosa.time_to_frames(2, sr=sr))) S_filter = np.minimum(S_full, S_filter) margin_i, margin_v = 2, 10 power = 2 mask_i = librosa.util.softmask(S_filter, margin_i * (S_full - S_filter), power=power) mask_v = librosa.util.softmask(S_full - S_filter, margin_v * S_filter, power=power) S_foreground = mask_v * S_full S_background = mask_i * S_full self.y_processed = librosa.istft(S_background) self.show_processed_data() def on_add_echo(self): if len(self.y_original) == 0: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Upload sound file") msg.setInformativeText( "First you should add sound file to process") msg.setWindowTitle("Error") msg.exec_() return num_shift = int(self.sampling_rate * self.echo_shift) zeros = np.zeros(num_shift) original = np.append(self.y_original, zeros) echo = np.append(zeros, self.y_original) * self.echo_alpha self.y_processed = original + echo np.delete( self.y_processed, np.arange( len(self.y_processed) - len(zeros), len(self.y_processed))) self.show_processed_data() def on_filter_echo(self): ceps = cepstrum.real_cepstrum(np.array(self.y_original)) index, result = CepstrumDialog.show_dialog(self, ceps) if result: print(index) b = np.array([1]) a = np.zeros(index + 1) a[0] = 1 a[len(a) - 1] = self.echo_alpha zi = signal.lfilter_zi(b, a) self.y_processed, _ = signal.lfilter(b, a, self.y_original, axis=0, zi=zi * self.y_original[0]) w1, h1 = signal.freqz(b, a) result = FilterResponseDialog.show_dialog(parent=self, w1=w1, h1=h1) if result: self.show_processed_data() ''' Filters ''' def on_fir_filter(self, filter_type, limit1, limit2, extra): if filter_type == "Low-pass": design_filter = signal.firwin(41, limit1, window=extra) elif filter_type == "High-pass": temp = np.zeros(41) temp[20] = 1 design_filter = temp - np.array( signal.firwin(41, limit1, window=extra)) elif filter_type == "Band-pass": temp = np.zeros(41) temp[20] = 1 design_filter = temp - np.array( signal.firwin(41, [limit1, limit2], window=extra)) elif filter_type == "Band-reject": design_filter = signal.firwin(41, [limit1, limit2], window=extra) self.y_processed = signal.convolve(self.y_original, design_filter, mode='same') w1, h1 = signal.freqz(design_filter) result = FilterResponseDialog.show_dialog(parent=self, w1=w1, h1=h1) if result: self.show_processed_data() def on_iir_filter(self, filter_type, limit1, limit2, extra, max_ripple, min_attenuation): if filter_type == "Low-pass": b, a = signal.iirfilter(4, limit1, rp=int(max_ripple), rs=int(min_attenuation), btype='lowpass', ftype=extra) elif filter_type == "High-pass": b, a = signal.iirfilter(4, limit1, rp=int(max_ripple), rs=int(min_attenuation), btype='highpass', ftype=extra) elif filter_type == "Band-pass": b, a = signal.iirfilter(4, [limit1, limit2], rp=int(max_ripple), rs=int(min_attenuation), btype='bandpass', ftype=extra) elif filter_type == "Band-reject": b, a = signal.iirfilter(4, [limit1, limit2], rp=int(max_ripple), rs=int(min_attenuation), btype='bandstop', ftype=extra) self.y_processed = signal.lfilter(b, a, self.y_original) w1, h1 = signal.freqz(b, a) result = FilterResponseDialog.show_dialog(parent=self, w1=w1, h1=h1) if result: self.show_processed_data() ''' Audio controls ''' def on_play(self): print("on_play") if len(self.y_processed) > 0: data2 = np.asarray(self.y_processed) sd.play(data2, self.sampling_rate) else: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Upload sound file") msg.setInformativeText( "First you should upload and process sound file to play") msg.setWindowTitle("Error") msg.exec_() def on_stop(self): sd.stop() def on_play_orig(self): print("on_play_orig") if len(self.y_original) > 0: data = np.asarray(self.y_original) sd.play(data, self.sampling_rate) else: msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Upload sound file") msg.setInformativeText("First you should add sound file to play") msg.setWindowTitle("Error") msg.exec_() ''' Signal plots ''' def show_original_data(self): # Time domain y_data_scaled = np.interp( self.y_original, (self.y_original.min(), self.y_original.max()), (-10, +10)) sample_size = len(self.y_original) self.x_data = np.linspace(0., 100., sample_size) points_1 = [] for k in range(len(y_data_scaled)): points_1.append(QPointF(self.x_data[k], y_data_scaled[k])) self.m_series_1.replace(points_1) # Frequency domain y_freq_data = np.abs(fftpack.fft(self.y_original)) y_freq_data = np.interp(y_freq_data, (y_freq_data.min(), y_freq_data.max()), (0, +10)) x_freq_data = fftpack.fftfreq(len( self.y_original)) * self.sampling_rate axis_x = QValueAxis() axis_x.setRange(0, self.sampling_rate / 2) axis_x.setLabelFormat("%g") axis_x.setTitleText("Frequency [Hz]") axis_y = QValueAxis() axis_y.setRange(np.min(y_freq_data), np.max(y_freq_data)) axis_y.setTitleText("Magnitude") self.m_chart_2.setAxisX(axis_x, self.m_series_2) self.m_chart_2.setAxisY(axis_y, self.m_series_2) points_2 = [] for k in range(len(y_freq_data)): points_2.append(QPointF(x_freq_data[k], y_freq_data[k])) self.m_series_2.replace(points_2) self.m_series_3.clear() self.m_series_4.clear() def show_processed_data(self): # Time domain y_data_scaled = np.interp( self.y_processed, (self.y_processed.min(), self.y_processed.max()), (-10, +10)) points_3 = [] sample_size = len(self.y_processed) x_data = np.linspace(0., 100., sample_size) for k in range(len(y_data_scaled)): points_3.append(QPointF(x_data[k], y_data_scaled[k])) self.m_series_3.replace(points_3) # Frequency domain y_freq_data = np.abs(fftpack.fft(self.y_processed)) y_freq_data = np.interp(y_freq_data, (y_freq_data.min(), y_freq_data.max()), (0, +10)) x_freq_data = fftpack.fftfreq(len( self.y_processed)) * self.sampling_rate axis_x = QValueAxis() axis_x.setRange(0, self.sampling_rate / 2) axis_x.setLabelFormat("%g") axis_x.setTitleText("Frequency [Hz]") axis_y = QValueAxis() axis_y.setRange(np.min(y_freq_data), np.max(y_freq_data)) axis_y.setTitleText("Magnitude") self.m_chart_4.setAxisX(axis_x, self.m_series_4) self.m_chart_4.setAxisY(axis_y, self.m_series_4) points_4 = [] for k in range(len(y_freq_data)): points_4.append(QPointF(x_freq_data[k], y_freq_data[k])) self.m_series_4.replace(points_4)
class ChartView(QChartView): def __init__(self, *args, **kwargs): super(ChartView, self).__init__(*args, **kwargs) self.resize(800, 600) self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 self.initChart() # 提示widget self.toolTipWidget = GraphicsProxyWidget(self._chart) # line 宽度需要调整 self.lineItem = QGraphicsLineItem(self._chart) pen = QPen(Qt.gray) self.lineItem.setPen(pen) self.lineItem.setZValue(998) self.lineItem.hide() # 一些固定计算,减少mouseMoveEvent中的计算量 # 获取x和y轴的最小最大值 axisX, axisY = self._chart.axisX(), self._chart.axisY() self.category_len = len(axisX.categories()) self.min_x, self.max_x = -0.5, self.category_len - 0.5 self.min_y, self.max_y = axisY.min(), axisY.max() # 坐标系中左上角顶点 self.point_top = self._chart.mapToPosition( QPointF(self.min_x, self.max_y)) def mouseMoveEvent(self, event): super(ChartView, self).mouseMoveEvent(event) pos = event.pos() # 把鼠标位置所在点转换为对应的xy值 x = self._chart.mapToValue(pos).x() y = self._chart.mapToValue(pos).y() index = round(x) # 得到在坐标系中的所有bar的类型和点 serie = self._chart.series()[0] bars = [ (bar, bar.at(index)) for bar in serie.barSets() if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y ] # print(bars) if bars: right_top = self._chart.mapToPosition( QPointF(self.max_x, self.max_y)) # 等分距离比例 step_x = round( (right_top.x() - self.point_top.x()) / self.category_len) posx = self._chart.mapToPosition(QPointF(x, self.min_y)) self.lineItem.setLine(posx.x(), self.point_top.y(), posx.x(), posx.y()) self.lineItem.show() try: title = self.categories[index] except: title = "" t_width = self.toolTipWidget.width() t_height = self.toolTipWidget.height() # 如果鼠标位置离右侧的距离小于tip宽度 x = pos.x() - t_width if self.width() - \ pos.x() - 20 < t_width else pos.x() # 如果鼠标位置离底部的高度小于tip高度 y = pos.y() - t_height if self.height() - \ pos.y() - 20 < t_height else pos.y() self.toolTipWidget.show(title, bars, QPoint(x, y)) else: self.toolTipWidget.hide() self.lineItem.hide() def handleMarkerClicked(self): marker = self.sender() # 信号发送者 if not marker: return bar = marker.barset() if not bar: return # bar透明度 brush = bar.brush() color = brush.color() alpha = 0.0 if color.alphaF() == 1.0 else 1.0 color.setAlphaF(alpha) brush.setColor(color) bar.setBrush(brush) # marker brush = marker.labelBrush() color = brush.color() alpha = 0.4 if color.alphaF() == 1.0 else 1.0 # 设置label的透明度 color.setAlphaF(alpha) brush.setColor(color) marker.setLabelBrush(brush) # 设置marker的透明度 brush = marker.brush() color = brush.color() color.setAlphaF(alpha) brush.setColor(color) marker.setBrush(brush) def handleMarkerHovered(self, status): # 设置bar的画笔宽度 marker = self.sender() # 信号发送者 if not marker: return bar = marker.barset() if not bar: return pen = bar.pen() if not pen: return pen.setWidth(pen.width() + (1 if status else -1)) bar.setPen(pen) def handleBarHoverd(self, status, index): # 设置bar的画笔宽度 bar = self.sender() # 信号发送者 pen = bar.pen() if not pen: return pen.setWidth(pen.width() + (1 if status else -1)) bar.setPen(pen) def initChart(self): self._chart = QChart(title="柱状图堆叠") self._chart.setAcceptHoverEvents(True) # Series动画 self._chart.setAnimationOptions(QChart.SeriesAnimations) self.categories = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"] names = ["邮件营销", "联盟广告", "视频广告", "直接访问", "搜索引擎"] series = QBarSeries(self._chart) for name in names: bar = QBarSet(name) # 随机数据 for _ in range(7): bar.append(randint(0, 10)) series.append(bar) bar.hovered.connect(self.handleBarHoverd) # 鼠标悬停 self._chart.addSeries(series) self._chart.createDefaultAxes() # 创建默认的轴 # x轴 axis_x = QBarCategoryAxis(self._chart) axis_x.append(self.categories) self._chart.setAxisX(axis_x, series) # chart的图例 legend = self._chart.legend() legend.setVisible(True) # 遍历图例上的标记并绑定信号 for marker in legend.markers(): # 点击事件 marker.clicked.connect(self.handleMarkerClicked) # 鼠标悬停事件 marker.hovered.connect(self.handleMarkerHovered) self.setChart(self._chart)
def bargraph(self, text): if text == "SKU Selection": # print("sku") self.clearLayout(lay) font = QtGui.QFont() font.setPointSize(9) comboBox = QtWidgets.QComboBox() comboBox.setGeometry(QtCore.QRect(20, 20, 171, 31)) comboBox.setObjectName("comboBox") comboBox.addItem("SKU Selection") comboBox.addItem("Analytics View") comboBox.activated[str].connect(self.bargraph) comboBox.setToolTip("Select an Option!") # Message to show when mouse hover comboBox.setFont(font) lay.addWidget(comboBox,0,Qt.AlignTop) self.dynamicaly_created_radiobtns() else: # print("analytics") try: self.clearLayout(lay) font = QtGui.QFont() font.setPointSize(9) set0 = QBarSet("Good") set1 = QBarSet("Bad") # set0.append([5400, 4900, 5100, 5400, 5000, 4800, 4500, 3000]) # set1.append([600, 800, 900, 600, 1000, 1200, 1500, 3000]) set0.append([z1,z2,z3,z4,z5,z6,z7,z8]) set1.append([y1,y2,y3,y4,y5,y6,y7,y8]) series = QStackedBarSeries() series.append(set0) series.append(set1) chart = QChart() chart.addSeries(series) chart.setTitle("Production Count") chart.setAnimationOptions(QChart.SeriesAnimations) categories = ["12:30", "13:00", "13:30", "14:00", "14:30", "15:00", "15:30", "16:00"] axis = QBarCategoryAxis() axis.append(categories) chart.createDefaultAxes() chart.setAxisX(axis, series) axisY = QValueAxis() axisY.setRange(0, 6000) axisY.setTitleText("Units") chart.setAxisY(axisY) chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) chartView = QChartView(chart) chartView.setRenderHint(QPainter.Antialiasing) chartView.setToolTip('Good Units v/s Bad Units') comboBox = QtWidgets.QComboBox() comboBox.setGeometry(QtCore.QRect(20, 20, 171, 31)) comboBox.setObjectName("comboBox") comboBox.addItem("SKU Selection") comboBox.addItem("Analytics View") comboBox.activated[str].connect(self.bargraph) comboBox.setToolTip("Select an Option!") # Message to show when mouse hover comboBox.setFont(font) lay.addWidget(comboBox,0) lay.addWidget(chartView,1) except: self.clearLayout(lay) font = QtGui.QFont() font.setPointSize(9) set0 = QBarSet("Good") set1 = QBarSet("Bad") # set0.append([5400, 4900, 5100, 5400, 5000, 4800, 4500, 3000]) # set1.append([600, 800, 900, 600, 1000, 1200, 1500, 3000]) set0.append([0,0,0,0,0,0,0,0]) set1.append([0,0,0,0,0,0,0,0]) series = QStackedBarSeries() series.append(set0) series.append(set1) chart = QChart() chart.addSeries(series) chart.setTitle("Production Count") chart.setAnimationOptions(QChart.SeriesAnimations) categories = ["12:30", "13:00", "13:30", "14:00", "14:30", "15:00", "15:30", "16:00"] axis = QBarCategoryAxis() axis.append(categories) chart.createDefaultAxes() chart.setAxisX(axis, series) axisY = QValueAxis() axisY.setRange(0, 6000) axisY.setTitleText("Units") chart.setAxisY(axisY) chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) chartView = QChartView(chart) chartView.setRenderHint(QPainter.Antialiasing) chartView.setToolTip('Good Units v/s Bad Units') comboBox = QtWidgets.QComboBox() comboBox.setGeometry(QtCore.QRect(20, 20, 171, 31)) comboBox.setObjectName("comboBox") comboBox.addItem("SKU Selection") comboBox.addItem("Analytics View") comboBox.activated[str].connect(self.bargraph) comboBox.setToolTip("Select an Option!") # Message to show when mouse hover comboBox.setFont(font) lay.addWidget(comboBox,0) lay.addWidget(chartView,1) msgBox = QMessageBox(self) msgBox.setIcon(QMessageBox.Information) msgBox.setText("Please Select a SKU!") msgBox.setWindowTitle("Application Info") msgBox.setStandardButtons(QMessageBox.Ok) returnValue = msgBox.exec() if returnValue == QMessageBox.Ok: print('OK clicked')
class Ui(mainwindow.Ui_MainWindow): def __init__(self, MainWindow): super(Ui, self).setupUi(MainWindow) self.MainWindow = MainWindow self.stix_tctm_parser = stix_parser.StixTCTMParser() self.initialize() def close(self): self.MainWindow.close() def style(self): return self.MainWindow.style() def initialize(self): self.tabWidget.setCurrentIndex(0) self.actionExit.triggered.connect(self.close) self.actionPlot.setEnabled(False) self.actionNext.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_ArrowForward)) self.actionPrevious.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_ArrowBack)) self.actionOpen.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_DialogOpenButton)) self.actionSave.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_DriveFDIcon)) self.actionSave.triggered.connect(self.save) self.actionOpen.triggered.connect(self.getOpenFilename) self.actionNext.triggered.connect(self.nextPacket) self.actionPrevious.triggered.connect(self.previousPacket) self.actionAbout.triggered.connect(self.about) self.actionPrevious.setEnabled(False) self.actionNext.setEnabled(False) self.actionSave.setEnabled(False) self.actionPlot.setEnabled(False) self.actionCopy.triggered.connect(self.onCopyTriggered) self.packetTreeWidget.currentItemChanged.connect(self.onPacketSelected) self.actionCopy.setEnabled(False) self.actionPaste.triggered.connect(self.onPasteTriggered) self.actionLog.triggered.connect(self.dockWidget.show) self.actionSetIDB.triggered.connect(self.onSetIDBClicked) self.plotButton.clicked.connect(self.onPlotButtonClicked) self.exportButton.clicked.connect(self.onExportButtonClicked) self.actionPlot.triggered.connect(self.onPlotActionClicked) self.actionLoadMongodb.triggered.connect(self.onLoadMongoDBTriggered) self.actionConnectTSC.triggered.connect(self.onConnectTSCTriggered) self.actionPacketFilter.triggered.connect(self.onPacketFilterTriggered) self.actionPlugins.triggered.connect(self.onPluginTriggered) self.actionOnlineHelp.triggered.connect(self.onOnlineHelpTriggered) self.actionViewBinary.triggered.connect(self.onViewBinaryTriggered) self.packetTreeWidget.customContextMenuRequested.connect( self.packetTreeContextMenuEvent) self.mdb = None self.current_row = 0 self.data = [] self.x = [] self.y = [] self.xlabel = 'x' self.ylabel = 'y' self.buttons_enabled = False self.chart = QChart() self.chart.layout().setContentsMargins(0, 0, 0, 0) self.chart.setBackgroundRoundness(0) self.savePlotButton.clicked.connect(self.savePlot) self.chartView = QChartView(self.chart) self.gridLayout.addWidget(self.chartView, 1, 0, 1, 15) #self.packetTreeWidget.itemDoubleClicked.connect(self.onPacketTreeItemDoubleClicked) self.selected_services = SELECTED_SERVICES self.selected_SPID = [] # IDB location self.settings = QtCore.QSettings('FHNW', 'stix_parser') self.idb_filename = self.settings.value('idb_filename', [], str) if self.idb_filename: idb._stix_idb.reload(self.idb_filename) if not idb._stix_idb.is_connected(): self.showMessage('IDB has not been set!') else: self.showMessage( 'IDB location: {} '.format(idb._stix_idb.get_idb_filename()), 1) def packetTreeContextMenuEvent(self, pos): menu = QtWidgets.QMenu() rawDataAction = menu.addAction('Show raw data') menu.addSeparator() filterAction = menu.addAction('Filter packets') copyPacketAction = menu.addAction('Copy packet') menu.addSeparator() deleteAllAction = menu.addAction('Delete all packets') self.current_row = self.packetTreeWidget.currentIndex().row() rawDataAction.triggered.connect(self.onViewBinaryTriggered) filterAction.triggered.connect(self.onPacketFilterTriggered) copyPacketAction.triggered.connect(self.onCopyTriggered) deleteAllAction.triggered.connect(self.onDeleteAllTriggered) action = menu.exec_(self.packetTreeWidget.viewport().mapToGlobal(pos)) def onDeleteAllTriggered(self): self.data.clear() self.packetTreeWidget.clear() self.paramTreeWidget.clear() #def onPacketTreeItemDoubleClicked(self): # self.onViewBinaryTriggered() def onViewBinaryTriggered(self): diag = QtWidgets.QDialog() diag_ui = raw_viewer.Ui_Dialog() diag_ui.setupUi(diag) if self.data: try: raw = self.data[self.current_row]['bin'] header = self.data[self.current_row]['header'] diag_ui.setPacketInfo('{}({},{}) {}'.format( header['TMTC'], header['service_type'], header['service_subtype'], header['DESCR'])) diag_ui.displayRaw(raw) except (IndexError, KeyError): diag_ui.setText('Raw data not available.') diag.exec_() def onOnlineHelpTriggered(self): webbrowser.open('https://github.com/i4Ds/STIX-dataviewer', new=2) def onPluginTriggered(self): self.plugin_location = self.settings.value('plugin_location', [], str) diag = QtWidgets.QDialog() diag_ui = plugin.Ui_Dialog() diag_ui.setupUi(diag) if self.plugin_location: diag_ui.setPluginLocation(self.plugin_location) diag_ui.setData(self.data, self.current_row) diag.exec_() location = diag_ui.getPluginLocation() if location != self.plugin_location: self.settings.setValue('plugin_location', location) def onPacketFilterTriggered(self): diag = QtWidgets.QDialog() diag_ui = packet_filter.Ui_Dialog() diag_ui.setupUi(diag) diag_ui.setSelectedServices(self.selected_services) diag_ui.buttonBox.accepted.connect( partial(self.applyServiceFilter, diag_ui)) diag.exec_() def applyServiceFilter(self, diag_ui): self.selected_SPID = diag_ui.getSelectedSPID() self.selected_services = diag_ui.getSelectedServices() self.showMessage('Applying filter...') self.addPacketsToView(self.data, True, show_stat=False) def onExportButtonClicked(self): if self.y: filename = str( QtWidgets.QFileDialog.getSaveFileName(None, "Save data to file", "", "CSV(*.csv)")[0]) if filename: with open(filename, 'w') as f: f.write('{},{}\n'.format(self.xlabel, self.ylabel)) for xx, yy in zip(self.x, self.y): f.write('{},{}\n'.format(xx, yy)) self.showMessage( 'The data has been written to {}'.format(filename)) else: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText('Plot first!') msgBox.setWindowTitle("Warning") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def savePlot(self): # if self.figure.get_axes(): if self.chart: filetypes = "PNG (*.png);;JPEG (*.jpg)" filename = str( QtWidgets.QFileDialog.getSaveFileName(None, "Save plot to file", "", filetypes)[0]) if filename: if not filename.endswith(('.png', '.jpg')): filename += '.png' # self.figure.savefig(filename) p = self.chartView.grab() p.save(filename) self.showMessage(('Saved to %s.' % filename)) else: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText('No figure to save') msgBox.setWindowTitle("STIX raw data viewer") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def onCopyTriggered(self): packet_id = self.current_row try: packet = self.data[packet_id] ss = pprint.pformat(packet) cb = QtWidgets.QApplication.clipboard() cb.clear(mode=cb.Clipboard) cb.setText(ss, mode=cb.Clipboard) msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Information) msg.setText( "The data of the selected packet has been copied to the clipboard." ) msg.setWindowTitle("Information") msg.setStandardButtons(QtWidgets.QMessageBox.Ok) retval = msg.exec_() except Exception as e: self.showMessage(str(e), 0) def onPasteTriggered(self): raw_hex = QtWidgets.QApplication.clipboard().text() if len(raw_hex) < 16: self.showMessage('No data in the clipboard.') return data_hex = re.sub(r"\s+", "", raw_hex) try: data_binary = binascii.unhexlify(data_hex) packets = self.stix_tctm_parser.parse_binary(data_binary, 0, store_binary=True) if not packets: return self.showMessage('%d packets read from the clipboard' % len(packets)) self.onDataReady(packets, clear=False, show_stat=False) except Exception as e: self.showMessageBox(str(e), data_hex) def showMessageBox(self, message, content): msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Critical) msg.setText("Error") msg.setInformativeText(message) msg.setWindowTitle("Error") msg.setDetailedText(content) msg.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) retval = msg.exec_() def showMessage(self, msg, where=0): if where != 1: self.statusbar.showMessage(msg) if where != 0: self.statusListWidget.addItem(msg) # if destination !=0 : # self.listWidget_2.addItem(msg) def onSetIDBClicked(self): self.idb_filename = QtWidgets.QFileDialog.getOpenFileName( None, 'Select file', '.', 'IDB file(*.db *.sqlite *.sqlite3)')[0] if not self.idb_filename: return idb._stix_idb.reload(self.idb_filename) if idb._stix_idb.is_connected(): #settings = QtCore.QSettings('FHNW', 'stix_parser') self.settings.setValue('idb_filename', self.idb_filename) self.showMessage( 'IDB location: {} '.format(idb._stix_idb.get_idb_filename()), 1) def save(self): filetypes = 'python compressed pickle (*.pklz);; python pickle file (*.pkl);; binary data (*.dat)' self.output_filename = str( QtWidgets.QFileDialog.getSaveFileName(None, "Save packets to", "", filetypes)[0]) if not self.output_filename.endswith(('.pklz', '.pkl', '.dat')): msg = 'unsupported file format !' self.showMessage(msg) return msg = 'Writing data to file %s' % self.output_filename self.showMessage(msg) if self.output_filename.endswith(('.pklz', '.pkl')): stw = stix_writer.StixPickleWriter(self.output_filename) stw.register_run(str(self.input_filename)) stw.write_all(self.data) #stw.done() elif self.output_filename.endswith('.dat'): stw = stix_writer.StixBinaryWriter(self.output_filename) stw.write_all(self.data) num_ok = stw.get_num_sucess() msg = ( 'The binary data of {} packets written to file {}, total packets {}' .format(num_ok, self.output_filename, len(self.data))) self.showMessage(msg) def setListViewSelected(self, row): #index = self.model.createIndex(row, 0); # if index.isValid(): # self.model.selectionModel().select( index, QtGui.QItemSelectionModel.Select) pass def about(self): msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText("STIX raw data parser and viewer, [email protected]") msgBox.setWindowTitle("Stix data viewer") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def nextPacket(self): self.current_row += 1 length = len(self.data) if self.current_row >= length: self.current_row = length - 1 self.showMessage('No more packet!') self.showPacket(self.current_row) self.setListViewSelected(self.current_row) def previousPacket(self): self.current_row -= 1 if self.current_row < 0: self.current_row = 0 self.showMessage('Reach the first packet!') self.showPacket(self.current_row) self.setListViewSelected(self.current_row) def getOpenFilename(self): filetypes = ( 'STIX raw data(*.dat *.bin *.binary);; python pickle files (*.pkl *pklz);;' 'ESA xml files (*xml);;' 'ESA ascii files(*.ascii);; CMDVS archive files (*.BDF);; All(*)') self.input_filename = QtWidgets.QFileDialog.getOpenFileName( None, 'Select file', '.', filetypes)[0] if not self.input_filename: return self.openFile(self.input_filename) def openFile(self, filename): self.packetTreeWidget.clear() msg = 'Loading file %s ...' % filename self.showMessage(msg) self.dataReader = StixFileReader(filename) #self.dataReader.dataLoaded.connect(self.onDataReady) self.dataReader.packetArrival.connect(self.onPacketArrival) self.dataReader.error.connect(self.onDataReaderError) self.dataReader.info.connect(self.onDataReaderInfo) self.dataReader.warn.connect(self.onDataReaderWarn) self.dataReader.start() def onDataReaderInfo(self, msg): self.showMessage(msg, 0) def onDataReaderWarn(self, msg): self.showMessage(msg, 1) def onDataReaderError(self, msg): self.showMessage(msg, 1) def onDataReady(self, data, clear=True, show_stat=True): if not clear: self.data.extend(data) else: self.data = data if data: self.addPacketsToView(data, clear=clear, show_stat=show_stat) self.enableButtons() def enableButtons(self): if not self.buttons_enabled: self.actionPrevious.setEnabled(True) self.actionNext.setEnabled(True) self.actionSave.setEnabled(True) self.actionCopy.setEnabled(True) self.actionPlot.setEnabled(True) self.actionViewBinary.setEnabled(True) self.buttons_enabled = True def addPacketsToView(self, data, clear=True, show_stat=True): if clear: self.packetTreeWidget.clear() #t0 = 0 for p in data: if type(p) is not dict: continue header = p['header'] root = QtWidgets.QTreeWidgetItem(self.packetTreeWidget) # if t0 == 0: # t0 = header['time'] timestamp_str = '' try: timestamp_str = header['utc'] except KeyError: timestamp_str = '{:.2f}'.format(header['time']) #- t0) root.setText(0, timestamp_str) root.setText( 1, '{}({},{}) - {}'.format(header['TMTC'], header['service_type'], header['service_subtype'], header['DESCR'])) if not self.selected_SPID: #not set then apply service if header['service_type'] not in self.selected_services: root.setHidden(True) else: if header['SPID'] not in self.selected_SPID: root.setHidden(True) if show_stat: total_packets = len(self.data) self.showMessage(('Total packet(s): %d' % total_packets)) def onConnectTSCTriggered(self): diag = QtWidgets.QDialog() diag_ui = tsc_connection.Ui_Dialog() diag_ui.setupUi(diag) self.tsc_host = self.settings.value('tsc_host', [], str) self.tsc_port = self.settings.value('tsc_port', [], str) if self.tsc_host: diag_ui.serverLineEdit.setText(self.tsc_host) if self.tsc_port: diag_ui.portLineEdit.setText(self.tsc_port) diag_ui.buttonBox.accepted.connect(partial(self.connectToTSC, diag_ui)) diag.exec_() def connectToTSC(self, dui): host = dui.serverLineEdit.text() port = dui.portLineEdit.text() self.showMessage('Connecting to TSC...') self.socketPacketReceiver = StixSocketPacketReceiver(host, int(port)) self.socketPacketReceiver.packetArrival.connect(self.onPacketArrival) self.socketPacketReceiver.error.connect(self.onDataReaderError) self.socketPacketReceiver.info.connect(self.onDataReaderInfo) self.socketPacketReceiver.warn.connect(self.onDataReaderWarn) self.socketPacketReceiver.start() def onPacketArrival(self, packets): if packets: self.onDataReady(packets, clear=False, show_stat=True) def onLoadMongoDBTriggered(self): diag = QtWidgets.QDialog() diag_ui = mongo_dialog.Ui_Dialog() diag_ui.setupUi(diag) #self.settings = QtCore.QSettings('FHNW', 'stix_parser') self.mongo_server = self.settings.value('mongo_server', [], str) self.mongo_port = self.settings.value('mongo_port', [], str) self.mongo_user = self.settings.value('mongo_user', [], str) self.mongo_pwd = self.settings.value('mongo_pwd', [], str) if self.mongo_server: diag_ui.serverLineEdit.setText(self.mongo_server) if self.mongo_port: diag_ui.portLineEdit.setText(self.mongo_port) if self.mongo_user: diag_ui.userLineEdit.setText(self.mongo_user) if self.mongo_pwd: diag_ui.pwdLineEdit.setText(self.mongo_pwd) diag_ui.pushButton.clicked.connect( partial(self.loadRunsFromMongoDB, diag_ui)) diag_ui.buttonBox.accepted.connect( partial(self.loadDataFromMongoDB, diag_ui, diag)) diag.exec_() def loadRunsFromMongoDB(self, dui): server = dui.serverLineEdit.text() port = dui.portLineEdit.text() user = dui.userLineEdit.text() pwd = dui.pwdLineEdit.text() self.showMessage('saving setting...') if self.mongo_server != server: self.settings.setValue('mongo_server', server) if self.mongo_port != port: self.settings.setValue('mongo_port', port) if self.mongo_user != user: self.settings.setValue('mongo_user', user) if self.mongo_pwd != pwd: self.settings.setValue('mongo_pwd', pwd) self.showMessage('connecting Mongo database ...') self.mdb = mgdb.MongoDB(server, int(port), user, pwd) if not self.mdb.is_connected(): return dui.treeWidget.clear() self.showMessage('Fetching data...') for run in self.mdb.get_runs(): root = QtWidgets.QTreeWidgetItem(dui.treeWidget) root.setText(0, str(run['_id'])) root.setText(1, run['filename']) root.setText(2, run['date']) root.setText(3, str(run['start'])) root.setText(4, str(run['end'])) def loadDataFromMongoDB(self, dui, diag): self.showMessage('Loading packets ...') selected_runs = [] for item in dui.treeWidget.selectedItems(): selected_runs.append(item.text(0)) if not selected_runs: self.showMessage('Run not selected!') if selected_runs: diag.done(0) self.showMessage('Loading data ...!') data = self.mdb.get_packets(selected_runs[0]) if data: self.onDataReady(data, clear=True) else: self.showMessage('No packets found!') # close def onPacketSelected(self, cur, pre): self.current_row = self.packetTreeWidget.currentIndex().row() self.showMessage(('Packet #%d selected' % self.current_row)) self.showPacket(self.current_row) def showPacket(self, row): if not self.data: return header = self.data[row]['header'] total_packets = len(self.data) self.showMessage( ('Packet %d / %d %s ' % (row, total_packets, header['DESCR']))) self.paramTreeWidget.clear() header_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget) header_root.setText(0, "Header") rows = len(header) for key, val in header.items(): root = QtWidgets.QTreeWidgetItem(header_root) root.setText(0, key) root.setText(1, str(val)) params = self.data[row]['parameters'] param_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget) param_root.setText(0, "Parameters") self.showParameterTree(params, param_root) self.paramTreeWidget.expandItem(param_root) self.paramTreeWidget.expandItem(header_root) self.current_row = row def showParameterTree(self, params, parent): if not params: return for p in params: root = QtWidgets.QTreeWidgetItem(parent) if not p: continue try: param_name = p['name'] desc = '' #description of parameter if 'desc' in p: desc = p['desc'] if not desc: desc = idb._stix_idb.get_PCF_description(param_name) scos_desc = idb._stix_idb.get_scos_description(param_name) if scos_desc: root.setToolTip(1, scos_desc) root.setText(0, param_name) root.setText(1, desc) root.setText(2, str(p['raw'])) try: root.setToolTip(2, hex(p['raw'][0])) except: pass root.setText(3, str(p['eng'])) if 'children' in p: if p['children']: self.showParameterTree(p['children'], root) except KeyError: self.showMessage( '[Error ]: keyError occurred when adding parameter') self.paramTreeWidget.itemDoubleClicked.connect(self.onTreeItemClicked) def walk(self, name, params, header, ret_x, ret_y, xaxis=0, data_type=0): if not params: return timestamp = header['time'] for p in params: if type(p) is not dict: continue if name == p['name']: values = None #print('data type:{}'.format(data_type)) if data_type == 0: values = p['raw'] else: values = p['eng'] try: yvalue = None if (type(values) is tuple) or (type(values) is list): yvalue = float(values[0]) else: yvalue = float(values) ret_y.append(yvalue) if xaxis == 1: ret_x.append(timestamp) else: self.showMessage(('Can not plot %s ' % str(yvalue))) except Exception as e: self.showMessage(('%s ' % str(e))) if 'children' in p: if p['children']: self.walk(name, p['children'], header, ret_x, ret_y, xaxis, data_type) def onPlotButtonClicked(self): if self.chart: self.chart.removeAllSeries() if not self.data: return self.showMessage('Preparing plot ...') name = self.paramNameEdit.text() packet_selection = self.comboBox.currentIndex() xaxis_type = self.xaxisComboBox.currentIndex() data_type = self.dataTypeComboBox.currentIndex() timestamp = [] self.y = [] packet_id = self.current_row params = self.data[packet_id]['parameters'] header = self.data[packet_id]['header'] current_spid = header['SPID'] if packet_selection == 0: self.walk(name, params, header, timestamp, self.y, xaxis_type, data_type) elif packet_selection == 1: for packet in self.data: header = packet['header'] if packet['header']['SPID'] != current_spid: continue params = packet['parameters'] self.walk(name, params, header, timestamp, self.y, xaxis_type, data_type) self.x = [] if not self.y: self.showMessage('No data points') elif self.y: style = self.styleEdit.text() if not style: style = '-' title = '%s' % str(name) desc = self.descLabel.text() if desc: title += '- %s' % desc self.chart.setTitle(title) ylabel = 'Raw value' xlabel = name if data_type == 1: ylabel = 'Engineering value' if xaxis_type == 0: if packet_selection == 1: xlabel = "Packet #" else: xlabel = "Repeat #" self.x = range(0, len(self.y)) if xaxis_type == 1: self.x = [t - timestamp[0] for t in timestamp] xlabel = 'Time -T0 (s)' if xaxis_type != 2: series = QLineSeries() series2 = None for xx, yy in zip(self.x, self.y): series.append(xx, yy) if 'o' in style: series2 = QScatterSeries() for xx, yy in zip(self.x, self.y): series2.append(xx, yy) self.chart.addSeries(series2) self.chart.addSeries(series) axisX = QValueAxis() axisX.setTitleText(xlabel) axisY = QValueAxis() axisY.setTitleText(ylabel) self.chart.setAxisX(axisX) self.chart.setAxisY(axisY) series.attachAxis(axisX) series.attachAxis(axisY) else: nbins = len(set(self.y)) ycounts, xedges = np.histogram(self.y, bins=nbins) series = QLineSeries() for i in range(0, nbins): meanx = (xedges[i] + xedges[i + 1]) / 2. series.append(meanx, ycounts[i]) self.chart.addSeries(series) axisX = QValueAxis() axisX.setTitleText(name) axisY = QValueAxis() axisY.setTitleText("Counts") self.chart.setAxisY(axisY) self.chart.setAxisX(axisX) series.attachAxis(axisX) series.attachAxis(axisY) self.xlabel = xlabel self.ylabel = ylabel self.chartView.setRubberBand(QChartView.RectangleRubberBand) self.chartView.setRenderHint(QtGui.QPainter.Antialiasing) msg = 'Y length: {}, min-Y: {}, max-Y: {}'.format( len(self.y), min(self.y), max(self.y)) self.showMessage(msg, 1) self.showMessage('The canvas updated!') def plotParameter(self, name=None, desc=None): self.tabWidget.setCurrentIndex(1) if name: self.paramNameEdit.setText(name) if desc: self.descLabel.setText(desc) def onPlotActionClicked(self): self.tabWidget.setCurrentIndex(1) self.plotParameter() def onTreeItemClicked(self, it, col): self.plotParameter(it.text(0), it.text(1))