class ChartView(QChartView): def __init__(self): QChartView.__init__(self) #self.resize(300, 300) self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 self.chart = QChart() self.seriesAcc = QLineSeries() self.seriesAcc.setName(CONF.leftUpNames[0]) self.chart.addSeries(self.seriesAcc) #声明并初始化X轴,Y轴 self.dtaxisX = QValueAxis() self.vlaxisY = QValueAxis() #设置坐标轴显示范围 self.dtaxisX.setMin(0) #self.dtaxisX.setMax(100) self.vlaxisY.setMin(0) #self.vlaxisY.setMax(100) self.dtaxisX.setTickCount(3) self.vlaxisY.setTickCount(3) #设置坐标轴名称 self.dtaxisX.setTitleText(CONF.leftUpNames[1]) self.vlaxisY.setTitleText(CONF.leftUpNames[2]) #设置网格不显示 self.vlaxisY.setGridLineVisible(False) #把坐标轴添加到chart中 self.chart.addAxis(self.dtaxisX, Qt.AlignBottom) self.chart.addAxis(self.vlaxisY, Qt.AlignLeft) self.seriesAcc.attachAxis(self.dtaxisX) self.seriesAcc.attachAxis(self.vlaxisY) self.initUI() def initUI(self): self.backend = BackendThread() self.backend.update_line.connect(self.handleLine) self.backend.start() def handleLine(self, data): if data[0] == 0: self.seriesAcc.clear() else: self.dtaxisX.setMax(data[0]) self.vlaxisY.setMax(data[0]) self.seriesAcc.clear() self.seriesAcc.append(0, 0) self.seriesAcc.append(data[0], data[1]) self.setChart(self.chart)
class Simulator(QtWidgets.QMainWindow, gui.Ui_MainWindow): def __init__(self): super().__init__() self.__car = car.motion() self.__car.param.fromJSON(car.defaultParams()) self.__canbus = None self.__translator = QTranslator(self) self.setupUi(self) self.positionChart = QChart() self.positionSeries = QLineSeries() self.speedChart = QChart() self.speedSeries = QLineSeries() self.fuelChart = QChart() self.fuelSeries = QLineSeries() self.engineChart = QChart() self.engineSeries = QLineSeries() self.positionChart.addSeries(self.positionSeries) self.speedChart.addSeries(self.speedSeries) self.fuelChart.addSeries(self.fuelSeries) self.engineChart.addSeries(self.engineSeries) self.positionChart.legend().hide() self.speedChart.legend().hide() self.fuelChart.legend().hide() self.engineChart.legend().hide() self.positionChart.createDefaultAxes() self.speedChart.createDefaultAxes() self.fuelChart.createDefaultAxes() self.engineChart.createDefaultAxes() self.positionChart.setTitle("Position") self.speedChart.setTitle("Speed") self.fuelChart.setTitle("Fuel") self.engineChart.setTitle("Engine") self.positionChart.setMargins(QMargins()) self.speedChart.setMargins(QMargins()) self.fuelChart.setMargins(QMargins()) self.engineChart.setMargins(QMargins()) self.positionChartW.setChart(self.positionChart) self.speedChartW.setChart(self.speedChart) self.fuelChartW.setChart(self.fuelChart) self.engineChartW.setChart(self.engineChart) self.populateFields() for file in os.listdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lang')): if file.startswith('carSim_') and file.endswith('.qm'): self.langSelector.addItem(file[7:-3]) def changeEvent(self, event): if event.type() == QEvent.LanguageChange: self.retranslateUi(self) super().changeEvent(event) @pyqtSlot(str) def on_langSelector_currentTextChanged(self, lang): if lang: self.__translator.load(QLocale(lang), 'carSim', '_', 'lang', '.qm') QtWidgets.QApplication.instance().installTranslator(self.__translator) else: QtWidgets.QApplication.instance().removeTranslator(self.__translator) @pyqtSlot() def on_resetSimulationButton_clicked(self): self.__car = car.motion() self.__car.param.fromJSON(car.defaultParams()) self.positionSeries.clear() self.speedSeries.clear() self.fuelSeries.clear() self.engineSeries.clear() self.populateFields() @pyqtSlot() def on_makeStepButton_clicked(self): try: # self.__car.setThrottle(self.engineField.text()) self.__car.makeStep() # motionMessage=can.Message(arbitration_id=18,is_extended_id=False,data=self.__car.getCanBytes()[:]) motionMessage = car.canMsg( StdId=18, Data=self.__car.getCanBytes()[:]) print(motionMessage) if (self.__canbus is not None and self.__canbus.connected == True): self.__canbus.sendMsg(motionMessage) with open('log.dat', 'a') as outfile: outfile.write("%.1f\t%f\t%f\t%f\n" % (self.__car.getSimTime(), self.__car.getSimDistance(), self.__car.getSimSpeed(), self.__car.getSimFuel())) except Exception as e: QtWidgets.QMessageBox.warning( self, _translate("Dialog", "Error"), str(e)) self.populateFields() if float(self.timeField.text()) >= 240: if self.simulationStart.isChecked() == True: self.simulationStart.click() def populateFields(self): self.timeField.setText(f"{self.__car.getSimTime():.2f}") self.positionField.setText(f"{self.__car.getSimDistance():.2f}") self.speedField.setText(f"{self.__car.getSimSpeed():.2f}") self.fuelField.setText(f"{self.__car.getSimFuel():.2f}") if (self.__canbus is not None and self.__canbus.connected == True): self.engineField.setText(f"{self.__car.getThrottle():.2f}") xax = self.positionChart.axisX() if(self.__car.getSimTime()>self.positionChart.axisX().max()): self.positionChart.axisX().setMax(self.__car.getSimTime()) self.speedChart.axisX().setMax(self.__car.getSimTime()) self.fuelChart.axisX().setMax(self.__car.getSimTime()) self.engineChart.axisX().setMax(self.__car.getSimTime()) #elif(self.__car.getSimTime()<self.positionChart.axisX().min()): # self.positionChart.axisX().setMin(self.__car.getSimTime()) # self.speedChart.axisX().setMin(self.__car.getSimTime()) # self.fuelChart.axisX().setMin(self.__car.getSimTime()) # self.engineChart.axisX().setMin(self.__car.getSimTime()) if(self.__car.getSimDistance()>self.positionChart.axisY().max()): self.positionChart.axisY().setMax(self.__car.getSimDistance()) elif(self.__car.getSimSpeed()<self.speedChart.axisY().min()): self.positionChart.axisY().setMin(self.__car.getSimDistance()) if(self.__car.getSimSpeed()>self.speedChart.axisY().max()): self.speedChart.axisY().setMax(self.__car.getSimSpeed()) elif(self.__car.getSimSpeed()<self.speedChart.axisY().min()): self.speedChart.axisY().setMin(self.__car.getSimSpeed()) if(self.__car.getSimFuel()>self.fuelChart.axisY().max()): self.fuelChart.axisY().setMax(self.__car.getSimFuel()) elif(self.__car.getSimFuel()<self.fuelChart.axisY().min()): self.fuelChart.axisY().setMin(self.__car.getSimFuel()) self.positionSeries.append(self.__car.getSimTime(),self.__car.getSimDistance()) self.speedSeries.append(self.__car.getSimTime(),self.__car.getSimSpeed()) self.fuelSeries.append(self.__car.getSimTime(),self.__car.getSimFuel()) self.engineSeries.append(self.__car.getSimTime(),self.__car.getThrottle()) @pyqtSlot(str) def on_engineField_textEdited(self,newEngineValue): try: self.__car.setThrottle(newEngineValue) except: pass @pyqtSlot() def on_actionAbout_triggered(self): self.AboutDialog = QtWidgets.QDialog() self.AboutDialog.ui = gui.Ui_AboutDialog() self.AboutDialog.ui.setupUi(self.AboutDialog) self.AboutDialog.setAttribute(Qt.WA_DeleteOnClose) self.AboutDialog.exec_() @pyqtSlot() def on_actionExport_Settings_triggered(self): filename, _ = QtWidgets.QFileDialog.getSaveFileName( self, _translate("Dialog", "Save Config"), ".", filter=_translate("Dialog", "Config Files (*.json)")+";;"+_translate("Dialog", "All Files(*.*)")) if filename: fp = open(filename, 'w') self.__car.param.toJSON(file=fp) fp.close() @pyqtSlot() def on_actionImport_Settings_triggered(self): filename, _ = QtWidgets.QFileDialog.getOpenFileName( self, _translate("Dialog", "Open Config"), ".", filter=_translate("Dialog", "Config Files (*.json)")+";;"+_translate("Dialog", "All Files(*.*)")) if filename: fp = open(filename, 'r') self.__car.param.fromJSON(fp) fp.close() @pyqtSlot() def on_refreshAvailablePorts_clicked(self): ports = serial.tools.list_ports.comports() self.availablePorts.clear() for port, desc, hwid in sorted(ports): self.availablePorts.addItem(desc, port) @pyqtSlot(bool) def on_connectPort_clicked(self, state): if state: try: self.__canbus = car.myCan(self.availablePorts.currentData(), [ self.canReceived.appendPlainText], [ self.__car.setSwitchingPoint], loopTime=0.025) self.__watch = QTimer(self) self.__watch.setInterval(1000) self.__watch.timeout.connect(self.syncTime) self.__watch.start() except Exception as e: self.connectPort.setChecked(False) QtWidgets.QMessageBox.critical( self, _translate("Dialog", "Error"), str(e)) else: self.connectPort.setText( _translate("MainWindow", "Disconnect")) self.canInterfaceTypes.setEnabled(False) self.availablePorts.setEnabled(False) self.refreshAvailablePorts.setEnabled(False) else: self.connectPort.setText(_translate("MainWindow", "Connect")) self.__canbus = None del(self.__watch) self.canInterfaceTypes.setEnabled(True) self.availablePorts.setEnabled(True) self.refreshAvailablePorts.setEnabled(True) @pyqtSlot() def syncTime(self): self.__canbus.sendMsg(car.canMsg(StdId=0, Data=int( self.__car.getSimTime()*1000.0).to_bytes(4, 'little'))) @pyqtSlot(bool) def on_simulationStart_clicked(self, state): if state: self.__ticker = QTimer(self) self.__ticker.setInterval(100) self.__ticker.timeout.connect(self.on_makeStepButton_clicked) self.__ticker.start() self.__car.setThrottle(1) self.simulationStart.setText( _translate("MainWindow", "Stop Simulation!")) lapData = 1 with open('log.dat', 'a') as outfile: outfile.write("%.1f\t%f\t%f\t%f\n" % (self.__car.getSimTime(), self.__car.getSimDistance(), self.__car.getSimSpeed(), self.__car.getSimFuel())) else: self.__ticker.stop() self.simulationStart.setText( _translate("MainWindow", "Start Simulation!")) lapData = 0 lapMessage = car.canMsg(StdId=32, Data=[lapData]) print(lapMessage) if (self.__canbus is not None and self.__canbus.connected == True): self.__canbus.sendMsg(lapMessage)
class Chart(QWidget): def __init__(self, ch0Name='ch0', ch1Name='ch1', ch2Name='ch2', ch3Name='ch3'): super(Chart, self).__init__() self.ch0Name = ch0Name self.ch1Name = ch1Name self.ch2Name = ch2Name self.ch3Name = ch3Name self.data = [] self.initUI() def initUI(self): vbox = QVBoxLayout() vbox.addWidget(self.chartUI()) vbox.addWidget(self.controlUI()) self.setLayout(vbox) def chartUI(self): self.chart1 = QLineSeries() self.chart1.setName(self.ch0Name) self.chart1.setPen(QPen(Qt.red)) self.chart2 = QLineSeries() self.chart2.setName(self.ch1Name) self.chart2.setPen(QPen(Qt.darkYellow)) self.chart3 = QLineSeries() self.chart3.setName(self.ch2Name) self.chart3.setPen(QPen(Qt.green)) self.chart4 = QLineSeries() self.chart4.setName(self.ch3Name) self.chart4.setPen(QPen(Qt.blue)) self.chart = QChart() self.chart.addSeries(self.chart1) self.chart.addSeries(self.chart2) self.chart.addSeries(self.chart3) self.chart.addSeries(self.chart4) self.chart.setAnimationOptions(QChart.AllAnimations) self.chart.setTitle("laser data") self.axis_x = QValueAxis() self.axis_y = QValueAxis() self.axis_x.setTickCount(10) self.axis_y.setTickCount(10) self.axis_x.setLabelFormat('%d') self.axis_y.setLabelFormat('%d') self.axis_x.setRange(0, 1000) self.axis_y.setRange(100, 300) self.chart.setAxisX(self.axis_x, self.chart1) self.chart.setAxisY(self.axis_y, self.chart1) self.chart.setAxisX(self.axis_x, self.chart2) self.chart.setAxisY(self.axis_y, self.chart2) self.chart.setAxisX(self.axis_x, self.chart3) self.chart.setAxisY(self.axis_y, self.chart3) self.chart.setAxisX(self.axis_x, self.chart4) self.chart.setAxisY(self.axis_y, self.chart4) self.chartView = QChartView() self.chartView.setChart(self.chart) self.chartView.setRubberBand(QChartView.RectangleRubberBand) self.chartView.setRenderHint(QPainter.Antialiasing) return self.chartView def controlUI(self): self.autoRefreshCb = QCheckBox('自动刷新') self.autoRefreshCb.setChecked(True) self.ch0Enable = QCheckBox(self.ch0Name) self.ch1Enable = QCheckBox(self.ch1Name) self.ch2Enable = QCheckBox(self.ch2Name) self.ch3Enable = QCheckBox(self.ch3Name) self.ch0Enable.setChecked(True) self.ch1Enable.setChecked(True) self.ch2Enable.setChecked(True) self.ch3Enable.setChecked(True) hbox1 = QHBoxLayout() hbox1.addWidget(self.autoRefreshCb) hbox1.addWidget(self.ch0Enable) hbox1.addWidget(self.ch1Enable) hbox1.addWidget(self.ch2Enable) hbox1.addWidget(self.ch3Enable) self.xDataMinLabel = QLabel('x min:') self.xDataMaxLabel = QLabel('x max:') self.yDataMinLabel = QLabel('y min:') self.yDataMaxLabel = QLabel('y max:') self.xDataMinLine = QLineEdit() self.xDataMaxLine = QLineEdit() self.yDataMinLine = QLineEdit() self.yDataMaxLine = QLineEdit() self.setBtn = QPushButton('设置') hbox = QHBoxLayout() hbox.addWidget(self.xDataMinLabel) hbox.addWidget(self.xDataMinLine) hbox.addWidget(self.xDataMaxLabel) hbox.addWidget(self.xDataMaxLine) hbox.addWidget(self.yDataMinLabel) hbox.addWidget(self.yDataMinLine) hbox.addWidget(self.yDataMaxLabel) hbox.addWidget(self.yDataMaxLine) hbox.addWidget(self.setBtn) mainLayout = QVBoxLayout() mainLayout.addLayout(hbox1) mainLayout.addLayout(hbox) frame = QFrame() frame.setLayout(mainLayout) self.setBtn.clicked.connect(self.setAxisRange) self.ch0Enable.clicked.connect(self.changeCh) self.ch1Enable.clicked.connect(self.changeCh) self.ch2Enable.clicked.connect(self.changeCh) self.ch3Enable.clicked.connect(self.changeCh) return frame # @timethis def update(self): x0Data, y0Data, x1Data, y1Data, x2Data, y2Data, x3Data, y3Data = self.data self.chart1.clear() self.chart2.clear() self.chart3.clear() self.chart4.clear() try: for i in range(0, len(x0Data)): if self.ch0Enable.isChecked(): self.chart1.append(QPoint(x0Data[i], y0Data[i])) for i in range(0, len(x1Data)): if self.ch1Enable.isChecked(): self.chart2.append(QPoint(x1Data[i], y1Data[i])) for i in range(0, len(x2Data)): if self.ch2Enable.isChecked(): self.chart3.append(QPoint(x2Data[i], y2Data[i])) for i in range(0, len(x3Data)): if self.ch3Enable.isChecked(): self.chart4.append(QPoint(x3Data[i], y3Data[i])) except: logging.debug('y0Data is %s' % y0Data) logging.debug('y1Data is %s' % y1Data) logging.debug('y2Data is %s' % y2Data) logging.debug('y3Data is %s' % y3Data) self.chart1.clear() self.chart2.clear() self.chart3.clear() self.chart4.clear() # self.chartView.update() if self.autoRefreshCb.isChecked(): self.fillAxisRange() self.setAxisRange() def fillAxisRange(self): x0Data, y0Data, x1Data, y1Data, x2Data, y2Data, x3Data, y3Data = self.data tmp = [min(x0Data), min(x1Data), min(x2Data), min(x3Data)] xDataMin = min(tmp) tmp = [max(x0Data), max(x1Data), max(x2Data), max(x3Data)] xDataMax = max(tmp) tmp = [min(y0Data), min(y1Data), min(y2Data), min(y3Data)] yDataMin = min(tmp) tmp = [max(y0Data), max(y1Data), max(y2Data), max(y3Data)] yDataMax = max(tmp) xDataAvr = (xDataMin + xDataMax) // 2 yDataAvr = (yDataMin + yDataMax) // 2 self.xDataMinLine.setText(str(xDataAvr - xDataAvr*6//5)) self.xDataMaxLine.setText(str(xDataAvr + xDataAvr*6//5)) self.yDataMinLine.setText(str(yDataAvr - yDataAvr*6//5)) self.yDataMaxLine.setText(str(yDataAvr + yDataAvr*6//5)) @pyqtSlot() def setAxisRange(self): if self.xDataMinLine.text() and self.xDataMaxLine.text() and \ self.yDataMinLine.text() and self.yDataMaxLine.text(): self.axis_x.setRange(int(self.xDataMinLine.text()), int(self.xDataMaxLine.text())) self.axis_y.setRange(int(self.yDataMinLine.text()), int(self.yDataMaxLine.text())) else: QMessageBox.warning(self, '警告', '缺少参数') @pyqtSlot() def changeCh(self): self.chart1.clear() self.chart2.clear() self.chart3.clear() self.chart4.clear() x0Data, y0Data, x1Data, y1Data, x2Data, y2Data, x3Data, y3Data = self.data for i in range(0, len(x0Data)): if self.ch0Enable.isChecked(): self.chart1.append(QPoint(x0Data[i], y0Data[i])) for i in range(0, len(x1Data)): if self.ch1Enable.isChecked(): self.chart2.append(QPoint(x1Data[i], y1Data[i])) for i in range(0, len(x2Data)): if self.ch2Enable.isChecked(): self.chart3.append(QPoint(x2Data[i], y2Data[i])) for i in range(0, len(x3Data)): if self.ch3Enable.isChecked(): self.chart4.append(QPoint(x3Data[i], y3Data[i]))
class ChartView(QChartView): def __init__(self, no_margins=False): super().__init__() self.__last_mouse_pos = None self.lineSeries = QLineSeries() # self.lineSeries.setPointsVisible(True) self.no_margins = no_margins #Create a menu by right click on QChartView self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.show_context_menu) ##Axis cofnig self.x_time_scaled = False self.x_name = None self.y_name = None self.x_tick_num = 30 self.y_tick_num = 20 self.x_label_angle = -90 self.y_label_angle = 0 self.y_min = None self.y_max = None self.x_min = None self.x_max = None def mousePressEvent(self, event: QMouseEvent): if event.button() == Qt.MiddleButton: self.__last_mouse_pos = event.pos() event.accept() QChartView.mousePressEvent(self, event) def mouseMoveEvent(self, event: QMouseEvent): if event.buttons() & Qt.MiddleButton: dif_pos = event.pos() - self.__last_mouse_pos self.chart().scroll(-dif_pos.x(), dif_pos.y()) event.accept() self.__last_mouse_pos = event.pos() QChartView.mouseMoveEvent(self, event) def wheelEvent(self, event: QWheelEvent): factor = event.angleDelta().y() if factor < 0: self.chart().zoom(0.75) else: self.chart().zoom(1.25) def show_context_menu(self, pos: QPoint): menu = QMenu() plot_save_action = QAction('Сохранить график', self) plot_save_action.triggered.connect(self.save_plot) menu.addAction(plot_save_action) menu.exec(self.mapToGlobal(pos)) def save_plot(self): pixmap = self.grab() pixmap_name = QFileDialog.getSaveFileName( self, 'Сохранение графика', None, "PNG (*.png);;JPG (*.jpg);;BMP (*.bmp)")[0] if not pixmap_name: return pixmap.save(pixmap_name, os.path.splitext(pixmap_name)[1][1:]) def make_axis(self): if self.x_time_scaled: axis_x = QDateTimeAxis() axis_x.setFormat("yyyy-MM-dd HH:mm:ss") axis_x.setTitleText("Время") else: axis_x = QValueAxis() axis_x.setTitleText(self.x_name) if self.x_min: axis_x.setMin(self.x_min) if self.x_max: axis_x.setMax(self.x_max) axis_x.setLabelsAngle(self.x_label_angle) axis_x.setTickCount(self.x_tick_num) axis_y = QValueAxis() if self.y_min: axis_y.setMin(self.y_min) if self.y_max: axis_y.setMax(self.y_max) axis_y.setTitleText(self.y_name) axis_y.setTickCount(self.y_tick_num) axis_y.setLabelsAngle(self.y_label_angle) return axis_x, axis_y def build_plot(self, data, title, is_legend_visible=False, series_name=None): axis_x, axis_y = self.make_axis() chart = QChart() if self.no_margins: chart.setMargins(QMargins(0, 0, 0, 0)) self.clean() if data != None: self.lineSeries = self.fill_series(data) self.lineSeries.setName(series_name) chart.addSeries(self.lineSeries) chart.legend().setVisible(is_legend_visible) chart.setTitle(title) chart.addAxis(axis_x, Qt.AlignBottom) chart.addAxis(axis_y, Qt.AlignLeft) self.lineSeries.attachAxis(axis_x) self.lineSeries.attachAxis(axis_y) self.setChart(chart) def build_multiple_plot(self, first_data, second_data, title): axis_x, axis_y = self.make_axis() chart = QChart() if self.no_margins: chart.setMargins(QMargins(0, 0, 0, 0)) self.clean() first_lineseries = self.fill_series(first_data) second_lineseries = self.fill_series(second_data) chart.addSeries(first_lineseries) chart.addSeries(second_lineseries) chart.addAxis(axis_x, Qt.AlignBottom) chart.addAxis(axis_y, Qt.AlignLeft) chart.legend().setVisible(False) chart.setTitle(title) first_lineseries.attachAxis(axis_x) first_lineseries.attachAxis(axis_y) second_lineseries.attachAxis(axis_x) second_lineseries.attachAxis(axis_y) first_lineseries.setPointsVisible(True) second_lineseries.setPointsVisible(True) self.setChart(chart) def series_append(self, x, y, x_min, y_min, x_range=False, y_range=False): self.lineSeries.append(x, y) self.chart().axisX().setMin(x_min) self.chart().axisY().setMin(y_min - 0.2) if x_range: self.chart().axisX().setMax(x) if y_range: self.chart().axisY().setMax(y + 0.2) def add_series(self, data, series_name=None): series = self.fill_series(data) series.setName(series_name) self.chart().addSeries(series) series.attachAxis(self.chart().axisX()) series.attachAxis(self.chart().axisY()) def fill_series(self, data): series = QLineSeries() series.setPointsVisible(True) xf = data[0] yf = data[1] for i in range(len(xf)): series.append(xf[i], yf[i]) return series def clean(self): self.lineSeries.clear()
class MainInterface(QMainWindow): '''实现 GUI 以及其连接整个程序的功能 @属性说明: @方法说明: @注意: ''' def __init__(self, parent=None): super().__init__(parent) # 父类初始化 self.ui = Ui_main_interface() # 创建UI类 self.ui.setupUi(self) self.showMaximized() # 最大化界面 self.setWindowTitle("疲劳检测") # 界面标题 self.time = QTimer() # 创建定时器 self._connect_slot() # 初始化槽函数连接 self.eye_data_chart_init() # 初始化眼睛闭合数据折线图 self._t = 0 # 用来计数 self._perclos_threshold = 0.3 # 设置初始化阈值 def _connect_slot(self): '''初始化信号与槽的连接 @参数说明: 无 @返回值: 无 @注意: 无 ''' self.ui.btn_start.clicked.connect(self._display) self.time.timeout.connect(self.test) def paintEvent(self, evevt): """绘图软件背景图 Qt里面默认事件处理函数,在界面需要重新绘制时触发 此方法主要是绘制软件的背景图 @参数说明: evevt:Qt默认事件 @返回值: 无 @注意: 无 """ temp_painter = QPainter(self) # 创建背景图QPixmap()对象 soft_background_image = QPixmap("resources/groud.jpg") # 绘制背景图 temp_painter.drawPixmap(0, 0, self.width(), self.height(), soft_background_image) # 执行父类的paintEvent()函数,以便父类执行其内建的一些操作 super().paintEvent(evevt) def eye_data_chart_init(self): '''初始化左右眼闭合程度曲线图表 @参数说明: 无 @返回值: 无 @注意: 无 ''' # 创建 chart 和 chartview self._chart = QChart() self._chart.setTitle("眼睛闭合程度值") # 设置图表标题 self._chartView = QChartView(self) self._chartView.setChart(self._chart) # chart 添加到 chartview # 完成布局 self.ui.horizontalLayout_2.addWidget(self._chartView) ## 创建曲线系列 self._series0 = QLineSeries() self._series1 = QLineSeries() self._series0.setName("左眼曲线") # 设置曲线名 self._series1.setName("右眼曲线") self._chart.addSeries(self._series0) # 序列添加到图表 self._chart.addSeries(self._series1) # 创建坐标轴 self._axis_x = QValueAxis() # x 轴 self._axis_x.setRange(0, 60) # 设置 x 轴坐标范围 self._axis_x.setTitleText("time(secs)") # x 轴标题 self._axis_y = QValueAxis() # y 轴 self._axis_y.setRange(0, 0.5) # 设置 y 轴坐标范围 self._axis_y.setTitleText("value") # y 轴标题 # 为序列设置坐标轴 self._chart.setAxisX(self._axis_x, self._series0) self._chart.setAxisY(self._axis_y, self._series0) self._chart.setAxisX(self._axis_x, self._series1) self._chart.setAxisY(self._axis_y, self._series1) @pyqtSlot(bool) def _display(self, checked): ''' @参数说明: @返回值: @注意: ''' if (checked == False): # 当按键为 False 时,停止检查 self.ui.le_eye_threshold.setEnabled(True) self.ui.btn_start.setText("启动") self.ui.lb_fatigue_detection.setText("检测停止") self.time.stop() else: threshold_str = self.ui.le_eye_threshold.text() # 获得睁闭眼阈值 threshold_str = re.match(r"\d\.\d\d$", threshold_str) # 对睁闭眼阈值做限定 if (threshold_str == None): message_title = "阈值格式错误" message_text = "请输入正确的阈值格式,格式为 x.xx (x 为数字)" QMessageBox.critical(self, message_title, message_text) else: self.ui.btn_start.setText("停止") self.ui.le_eye_threshold.setEnabled(False) model_path = r"E:\make_data\facial" # 人脸关键模型路径 opencv_facial_path = r"E:\Fatigue_Detection\model_data\facial_model\haarcascade_frontalface_default.xml" # 人脸检测模型路径 cap_index = eval( self.ui.cmb_cap_index.currentText()) # 从 combabox 获取设想头索引 # cap_file = r"C:\Users\LWL\Pictures\Camera Roll\WIN_20200503_07_51_19_Pro.mp4" self.facial_data = GetFacialData( tf_model=model_path, facial_model_file=opencv_facial_path, cap_index=cap_index) # 创建 GetFacialData 类 self.time.start(1000) # 设置每次检测的间隔时间 self.ui.lb_fatigue_detection.setText("检测中") self._series0.clear() self._series1.clear() self._t = 0 # 重新开始计数 self._perclos_threshold = eval(threshold_str.group()) def test(self): ret, frame = self.facial_data.get_frame() if (ret == -1): message_title = "摄像头打开失败" message_text = "摄像头打开失败,请检测摄像头索引是否正确" self.time.stop() QMessageBox.critical(self, message_title, message_text) if (ret == -2): message_title = "获取帧失败" message_text = "从摄像头获取帧失败,请重启软件" self.time.stop() QMessageBox.critical(self, message_title, message_text) else: frame = cv2.medianBlur(frame, 5) ret, rect = self.facial_data.get_facial(frame) h, w = frame.shape[:2] if (ret == -1): frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) temp_q_image = QImage(frame, w, h, QImage.Format_RGB888) # 在 lb_display 中显示图片 self.ui.lb_display.setPixmap(QPixmap.fromImage(temp_q_image)) self.ui.lb_facial_number.setText(str(rect)) # 显示当前检测人脸数 else: self.ui.lb_facial_number.setText("1") # 显示当前检测人脸数 frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) facial = frame[rect[2]:rect[3], rect[0]:rect[1]] image = cv2.cvtColor(facial, cv2.COLOR_RGB2GRAY) image_h, image_w = image.shape[:2] image = cv2.resize(image, (150, 150)) image = np.reshape(image, [1, 150, 150, 1]) image = image / 255. facial_keypoints = self.facial_data.get_result(image) facial_keypoints = (facial_keypoints[0] + 1) / 2 facial_keypoints[:: 2] = facial_keypoints[::2] * image_w + rect[0] facial_keypoints[ 1::2] = facial_keypoints[1::2] * image_h + rect[2] # 圈出人脸范围 facial = cv2.rectangle(frame, (rect[0], rect[2]), (rect[1], rect[3]), (255, 0, 0), 1) # 将人脸多特征点标记到图片上 for i in range(12): facial = cv2.circle(frame, (int(facial_keypoints[2 * i]), int(facial_keypoints[2 * i + 1])), 2, (255, 0, 0), -1) # 进行打点 # 获得左右眼的闭合程度 eye_process_data = result_process.eyes_process( facial_keypoints) # 添加点到图表上 self._series0.append(self._t, eye_process_data[0]) self._series1.append(self._t, eye_process_data[1]) # 在label上显示左右眼计算值 self.ui.lb_left_eye_figure.setText(str( eye_process_data[0])) # 显示左眼的计算值 self.ui.lb_right_eye_figure.setText(str( eye_process_data[1])) # 显示右眼的计算值 temp_q_image = QImage(facial.data, w, h, QImage.Format_RGB888) # 在 lb_display 中显示图片 self.ui.lb_display.setPixmap(QPixmap.fromImage(temp_q_image)) self._t += 1 # 每个 1s 绘制点 if (self._t > 60): # 获取 series0 以及 series1 中所有的数据 left_eye_point = self._series0.pointsVector() right_eye_point = self._series1.pointsVector() # 使用正则表达式提取其中的计算数据 left_eye_data = np.array( re.findall(r"\d+\.\d{2,5}", str(left_eye_point))) right_eye_data = np.array( re.findall(r"\d+\.\d{2,5}", str(right_eye_point))) perclos_judge_result = result_process.perclos_judge( left_eye_data, self._perclos_threshold) # 对 perclos_judge 的结果进行输出 if (perclos_judge_result == -2): self.ui.lb_fatigue_detection.setText("过度疲劳") message_title = "过度疲劳警告提醒" message_text = "为了你的人身安全,请停下手中工作,休息一段时间!" QMessageBox.critical(self, message_title, message_text) # 过度疲劳提醒 elif (perclos_judge_result == -1): self.ui.lb_fatigue_detection.setText("疲劳状态") else: self.ui.lb_fatigue_detection.setText("状态良好") # 清除所有的 series0 以及 series1 数据,重新绘图 self._series0.clear() self._series1.clear() self._t = 0 # 重新开始计数
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 = data[0] y_axis = data[1] 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']))
class Demo(QWidget,QFont): def __init__(self): super(Demo,self).__init__() #子类调用父类的方法 self.BuildUI() #构造界面 self.CreatChart() #创建曲线 self.connection() ##==============自定义功能函数============ def BuildUI(self): ##构造界面 self.resize(800, 600) self.setFont(QFont("黑体", 10.5)) ##设置字体 self.setWindowTitle("水工标准设计反应谱v1.1(Hs小毕)") ##s设置窗口标题 self.setWindowIcon(QIcon(':/fyp.ico')) # 定义控件 #定义“输入”控件=========================== self.label1 = QLabel("水工建筑物类型", self) # 标准设计反应谱最大值的代表值 BEITA = ["土石坝", "重力坝", "拱坝", "水闸|进水塔|边坡|其他"] self.comboBox1 = QComboBox() self.comboBox1.addItems(BEITA) self.comboBox1.setCurrentIndex(1) # 设置默认值 self.label2 = QLabel("特征周期Tg(s)", self) self.line2 = QLineEdit() self.line2.setPlaceholderText("单位(s)") self.label3 = QLabel("加速度幅值A(g)", self) self.line3 = QLineEdit() self.line3.setPlaceholderText("单位(g)") self.groupbox_1 = QGroupBox('输入数据', self) self.v_layout = QVBoxLayout() self.v_layout.addWidget(self.label1) self.v_layout.addWidget(self.comboBox1) self.v_layout.addWidget(self.label2) self.v_layout.addWidget(self.line2) self.v_layout.addWidget(self.label3) self.v_layout.addWidget(self.line3) self.groupbox_1.setLayout(self.v_layout) #定义“生成”控件=========================== self.v1_layout = QVBoxLayout() self.groupbox_2 = QGroupBox('生成曲线', self) self.button1 = QPushButton("生成曲线",self) self.v1_layout.addWidget(self.button1) self.groupbox_2.setLayout(self.v1_layout) #定义“输出单位”控件 self.groupbox_3 = QGroupBox('输出单位', self) self.h_layout = QHBoxLayout() self.unit_g = QRadioButton('g', self) self.unit_g.setChecked(True) #设置单位“g”为默认 self.unit_m = QRadioButton('m/sec2', self) self.unit_mm = QRadioButton('mm/sec2', self) self.h_layout.addWidget(self.unit_g) self.h_layout.addWidget(self.unit_m) self.h_layout.addWidget(self.unit_mm) self.groupbox_3.setLayout(self.h_layout) #定义输出的列数 self.groupbox_4 = QGroupBox('输出列数', self) self.h1_layout = QHBoxLayout() self.list2 = QRadioButton('时间|谱值') self.list2.setChecked(True) # 设置时间|谱值为默认 self.list1 = QRadioButton('谱值') self.h1_layout.addWidget(self.list2) self.h1_layout.addWidget(self.list1) self.groupbox_4.setLayout(self.h1_layout) #输出时间间隔 self.groupbox_5 = QGroupBox('输出时间间隔(s)', self) self.v2_layout = QHBoxLayout() self.Dtime = QRadioButton('0.01') self.Dtime.setChecked(True) # 设置0.01s为默认 self.Dtime_Edit = QLineEdit() self.Dtime1 = QRadioButton(self.Dtime_Edit) self.v2_layout.addWidget(self.Dtime) self.v2_layout.addWidget(self.Dtime1) self.v2_layout.addWidget(self.Dtime_Edit) self.groupbox_5.setLayout(self.v2_layout) #定义输出的文件选择 self.groupbox_6 = QGroupBox('输出文件', self) self.v3_layout = QVBoxLayout() self.h3_layout = QHBoxLayout() self.Enter = QPushButton('点击输出',self) self.out_line=QLineEdit() self.out_select=QPushButton('浏览',self) self.h3_layout.addWidget(self.out_line) self.h3_layout.addWidget(self.out_select) self.v3_layout.addLayout(self.h3_layout) self.v3_layout.addWidget(self.Enter) self.groupbox_6.setLayout(self.v3_layout) # 定义绘图控件=========================== self.chartView = QChartView() # 创建 ChartView self.v6_layout = QVBoxLayout() self.v6_layout.addWidget(self.groupbox_1) self.v6_layout.addWidget(self.groupbox_2) self.v6_layout.addWidget(self.groupbox_3) self.v6_layout.addWidget(self.groupbox_4) self.v6_layout.addWidget(self.groupbox_5) self.v6_layout.addWidget(self.groupbox_6) self.h4_layout = QHBoxLayout() self.h4_layout.addLayout(self.v6_layout) self.h4_layout.addWidget(self.chartView) self.setLayout(self.h4_layout) def CreatChart(self): self.chart = QChart() # 创建 Chart self.chart.setTitle("反应谱曲线") self.chartView.setChart(self.chart) # Chart添加到ChartView # 创建曲线序列 self.series0 = QLineSeries() self.chart.addSeries(self.series0) # 序列添加到图表 self.chart.createDefaultAxes() ##创建坐标轴 self.axisX = QValueAxis() # X 轴 self.axisX.setTitleText("T(s)") # 标题 self.axisX.setLabelFormat("%.1f") #标签格式 self.axisX.setTickCount(5) #主分隔个数 self.axisX.setMinorTickCount(4) self.axisY=QValueAxis() # Y 轴 self.axisY.setTitleText("Se/g") self.axisY.setTickCount(5) self.axisY.setMinorTickCount(4) self.axisY.setLabelFormat("%.2f") #标签格式 # 为序列设置坐标轴 self.chart.setAxisX(self.axisX, self.series0) # 为序列设置坐标轴 self.chart.setAxisY(self.axisY, self.series0) def PrepareData(self): """ 定义绘图中的数据 """ # 序列添加数值 Str_dict={"土石坝":1.6, "重力坝":2, "拱坝":2.5, "水闸|进水塔|边坡|其他":2.25} Str_list =self.comboBox1.currentText() BETA_Data = Str_dict[Str_list] # 特征周期 TG = float(self.line2.text()) # 加速度幅值 PGA = float(self.line3.text()) t = 0 intv = 0.01 pointCount = 301 self.series0.clear() #清楚数据======== for T in range(pointCount): T= round(T * intv,2) if T <= 0.1: y1 = (BETA_Data-1)/(0.1-0)*T+1 elif 0.1<T and T <= TG: y1= BETA_Data else: y1 = BETA_Data*(TG/T)**0.6 y1 = y1 * PGA self.series0.append(T, y1) #设置坐标轴范围========================= self.axisX.setRange(0,3) self.axisY.setRange(0,BETA_Data*PGA+1) def outputfilename(self): """ 定义输出路径 """ dlg = QFileDialog() filt = "Srf Files(*.Srf);;Text Files (*.txt);;All Files (*)" fileName, _ =dlg.getSaveFileName(self, "另存为文件", ".",filt) self.out_line.setText(fileName) def Output_Data(self): """ 定义输出的数据 """ # 序列添加数值 Str_dict={"土石坝":1.6, "重力坝":2, "拱坝":2.5, "水闸|进水塔|边坡|其他":2.25} Str_list =self.comboBox1.currentText() BETA_Data = Str_dict[Str_list] # 特征周期 TG = float(self.line2.text()) # 加速度幅值 PGA = float(self.line3.text()) # 输出单位 if self.unit_g.isChecked()==True: UNIT = 1 elif self.unit_g.isChecked()==True: UNIT = 9.81 else: UNIT = 9810 # 输出间隔 if self.Dtime.isChecked()==True: intv = 0.01 else: intv = float(self.Dtime_Edit.text()) pointCount = 301 fileNameout = QLineEdit.text(self.out_line) with open(fileNameout, 'w', encoding='gbk') as f_out: for T in range(pointCount): T = round(T * intv, 2) if T <= 0.1: y1 = (BETA_Data - 1) / (0.1 - 0) * T + 1 elif 0.1 < T and T <= TG: y1 = BETA_Data else: y1 = BETA_Data * (TG / T) ** 0.6 y1 = y1 * PGA * UNIT if self.list2.isChecked()==True: f_out.write(f"{T}\t\t{y1}\n") else: f_out.write(f"{y1}\n") def connection(self): """ 槽函数 """ self.button1.clicked.connect(lambda: self.PrepareData()) self.out_select.clicked.connect(lambda: self.outputfilename()) self.Enter.clicked.connect(lambda: self.Output_Data())
class AmzHistoryChart(QWidget): """A chart that graphs the history of an AmazonListing's sales rank, price, and number of offers.""" def __init__(self, parent=None): super(AmzHistoryChart, self).__init__(parent=parent) self.dbsession = Session() self.context_menu_actions = [] self._avg_pointspan = 0 self._max_points = 100 self.source = None self.history = None layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) # Set up the chart self.chart_view = QChartView(self) self.chart_view.setRenderHint(QPainter.Antialiasing) self.chart_view.setContextMenuPolicy(Qt.CustomContextMenu) self.chart_view.customContextMenuRequested.connect(self.context_menu) self.chart = QChart() self.chart.legend().hide() self.chart.setFlags(QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsSelectable) self.chart.installEventFilter(self) self.chart_view.setChart(self.chart) self.layout().addWidget(self.chart_view) # Create the axes rcolor = QColor(50, 130, 220) pcolor = QColor(0, 200, 0) ocolor = QColor(255, 175, 0) self.timeAxis = QDateTimeAxis() self.timeAxis.setFormat('M/dd hh:mm') self.timeAxis.setTitleText('Date/Time') self.chart.addAxis(self.timeAxis, Qt.AlignBottom) self.timeAxis.minChanged.connect(self.on_timeaxis_min_changed) self.rankAxis = QValueAxis() self.rankAxis.setLabelFormat('%\'i') self.rankAxis.setTitleText('Sales Rank') self.rankAxis.setLinePenColor(rcolor) self.rankAxis.setLabelsColor(rcolor) self.chart.addAxis(self.rankAxis, Qt.AlignLeft) self.priceAxis = QValueAxis() self.priceAxis.setLabelFormat('$%.2f') self.priceAxis.setTitleText('Price') self.priceAxis.setLinePenColor(pcolor) self.priceAxis.setLabelsColor(pcolor) self.chart.addAxis(self.priceAxis, Qt.AlignRight) # Create the series self.rankLine = QLineSeries() self.chart.addSeries(self.rankLine) self.rankLine.attachAxis(self.timeAxis) self.rankLine.attachAxis(self.rankAxis) self.rankLine.setColor(rcolor) self.priceLine = QLineSeries() self.chart.addSeries(self.priceLine) self.priceLine.attachAxis(self.timeAxis) self.priceLine.attachAxis(self.priceAxis) self.priceLine.setColor(pcolor) self.salesPoints = QScatterSeries() self.chart.addSeries(self.salesPoints) self.salesPoints.attachAxis(self.timeAxis) self.salesPoints.attachAxis(self.rankAxis) self.salesPoints.setColor(ocolor) def add_context_action(self, action): """Add an action to the chart's context menu.""" self.context_menu_actions.append(action) def add_context_actions(self, actions): """Adds all action in an iterable.""" self.context_menu_actions.extend(actions) def remove_context_action(self, action): """Removes an action from the chart's context menu.""" self.context_menu_actions.remove(action) def context_menu(self, point): """Show a context menu on the chart.""" menu = QMenu(self) menu.addActions(self.context_menu_actions) point = self.chart_view.viewport().mapToGlobal(point) menu.popup(point) def set_source(self, source): """Set the source listing for the graph.""" self.source = source # Update the chart self.rankLine.clear() self.priceLine.clear() self.salesPoints.clear() self.history = None start_date = datetime.utcnow() - timedelta(days=5) self.load_history_from(start_date) self.reset_axes() def load_history_from(self, start_date=datetime.utcfromtimestamp(0)): """Load history data from start-present.""" if not self.source: self._avg_pointspan = 0 return # Get the earliest point already in the chart points = self.rankLine.pointsVector() if points: # The chart is drawn right-to-left, so the last point is the earliest point earliest_msecs = points[-1].x() earliest = datetime.fromtimestamp(earliest_msecs / 1000, timezone.utc) if earliest <= start_date: return else: earliest = datetime.now(timezone.utc) # Get the product history stats if we don't already have them if self.history is None: self.history = dbhelpers.ProductHistoryStats(self.dbsession, self.source.id) # Start adding points to the chart last_row = None for row in self.dbsession.query(AmzProductHistory).\ filter(AmzProductHistory.amz_listing_id == self.source.id, AmzProductHistory.timestamp > start_date.replace(tzinfo=None), AmzProductHistory.timestamp < earliest.replace(tzinfo=None)).\ order_by(AmzProductHistory.timestamp.desc()): # SqlAlchemy returns naive timestamps time = row.timestamp.replace(tzinfo=timezone.utc).timestamp() * 1000 self.rankLine.append(time, row.salesrank or 0) self.priceLine.append(time, row.price or 0) if last_row: # It's possible for salesrank to be None try: slope = (last_row.salesrank - row.salesrank) / (last_row.timestamp.timestamp() - row.timestamp.timestamp()) if slope < -0.3: self.salesPoints.append(last_row.timestamp.replace(tzinfo=timezone.utc).timestamp() * 1000, last_row.salesrank) except (TypeError, AttributeError): pass last_row = row # Calculate the average span between points spans = 0 for p1, p2 in itertools.zip_longest(itertools.islice(points, 0, None, 2), itertools.islice(points, 1, None, 2)): if p1 and p2: spans += abs(p1.x() - p2.x()) self._avg_pointspan = spans // 2 def on_timeaxis_min_changed(self, min): """Respond to a change in the time axis' minimum value.""" # toTime_t() converts to UTC automatically utc_min = datetime.fromtimestamp(min.toTime_t(), timezone.utc) self.load_history_from(start_date=utc_min - timedelta(days=1)) def reset_axes(self): """Resets the chart axes.""" r = self.rankLine.pointsVector() p = self.priceLine.pointsVector() # If there is only one data point, set the min and max to the day before and the day after if len(r) == 1: tmin = QDateTime.fromMSecsSinceEpoch(r[0].x(), Qt.LocalTime).addDays(-1) tmax = QDateTime.fromMSecsSinceEpoch(r[0].x(), Qt.LocalTime).addDays(+1) else: tmin = min(r, key=lambda pt: pt.x(), default=QPointF(QDateTime.currentDateTime().addDays(-1).toMSecsSinceEpoch(), 0)).x() tmax = max(r, key=lambda pt: pt.x(), default=QPointF(QDateTime.currentDateTime().addDays(+1).toMSecsSinceEpoch(), 0)).x() tmin = QDateTime.fromMSecsSinceEpoch(tmin, Qt.LocalTime) tmax = QDateTime.fromMSecsSinceEpoch(tmax, Qt.LocalTime) self.timeAxis.setMin(tmin) self.timeAxis.setMax(tmax) # Find the min and max values of the series min_point = lambda pts: min(pts, key=lambda pt: pt.y(), default=QPointF(0, 0)) max_point = lambda pts: max(pts, key=lambda pt: pt.y(), default=QPointF(0, 0)) rmin = min_point(r) rmax = max_point(r) pmin = min_point(p) pmax = max_point(p) # Scale the mins and maxes to 'friendly' values scalemin = lambda v, step: ((v - step / 2) // step) * step scalemax = lambda v, step: ((v + step / 2) // step + 1) * step # The the axis bounds rmin = max(scalemin(rmin.y(), 1000), 0) rmax = scalemax(rmax.y(), 1000) pmin = max(scalemin(pmin.y(), 5), 0) pmax = scalemax(pmax.y(), 5) self.rankAxis.setMin(rmin) self.rankAxis.setMax(rmax) self.priceAxis.setMin(pmin) self.priceAxis.setMax(pmax) def eventFilter(self, watched, event): """Intercept and handle mouse events.""" if event.type() == QEvent.GraphicsSceneWheel and event.orientation() == Qt.Vertical: factor = 0.95 if event.delta() < 0 else 1.05 self.chart.zoom(factor) return True if event.type() == QEvent.GraphicsSceneMouseDoubleClick: self.chart.zoomReset() self.reset_axes() return True if event.type() == QEvent.GraphicsSceneMouseMove: delta = event.pos() - event.lastPos() self.chart.scroll(-delta.x(), delta.y()) return True return False
class VolumeFigure: def __init__(self, name): self.chart_volume_view = QChartView() self.chart_volume_view.chart().setLocalizeNumbers(True) self.volume_series = QCandlestickSeries() self.volume_time_axis = QDateTimeAxis() self.volume_time_axis.setFormat('h:mm') self.volume_axis = QValueAxis() self.volume_average_series = QLineSeries() self.chart_volume_view.chart().addSeries(self.volume_series) self.chart_volume_view.chart().addSeries(self.volume_average_series) self.chart_volume_view.chart().addAxis(self.volume_time_axis, Qt.AlignBottom) self.chart_volume_view.chart().addAxis(self.volume_axis, Qt.AlignLeft) self.chart_volume_view.chart().legend().hide() self.chart_volume_view.setRenderHint(QPainter.Antialiasing) def in_datetime_range(self, q): return self.datetime_range[0] < q < self.datetime_range[1] def get_chart_view(self): return self.chart_volume_view def clear_series_data(self): self.volume_series.clear() self.volume_average_series.clear() def set_volume_average(self, vol): self.volume_average_series.append(self.datetime_range[0], vol) self.volume_average_series.append(self.datetime_range[1], vol) def set_datetime(self, d): self.chart_datetime = d self.datetime_range = (d.timestamp() * 1000, d.replace(hour=23, minute=59).timestamp() * 1000) start_time = QDateTime() until_time = QDateTime() start_time.setDate(QDate(d.year, d.month, d.day)) until_time.setDate(QDate(d.year, d.month, d.day)) start_time.setTime(QTime(9, 0)) until_time.setTime(QTime(16, 0)) self.volume_time_axis.setRange(start_time, until_time) def add_volume_data(self, q, volume): if self.in_datetime_range(q): self.volume_series.append(QCandlestickSet(0, volume, 0, volume, q)) def attach(self): self.volume_time_axis.setTickCount(7) self.volume_average_series.attachAxis(self.volume_axis) self.volume_average_series.attachAxis(self.volume_time_axis) self.volume_series.attachAxis(self.volume_axis) self.volume_series.attachAxis(self.volume_time_axis) def set_max(self, vol): self.volume_axis.setRange(0, vol)
class PriceFigure: def __init__(self, name): self.name = name self.chart_view = QChartView() self.price_time_axis = QDateTimeAxis() self.price_time_axis.setFormat('h:mm') self.price_axis = QValueAxis() self.candle_stick_series = QCandlestickSeries() self.candle_stick_series.setIncreasingColor(Qt.red) self.candle_stick_series.setDecreasingColor(Qt.blue) self.moving_average_series = QLineSeries() self.top_edge_series = QScatterSeries() self.bottom_edge_series = QScatterSeries() self.trend_lines = [] self.short_top_trend_series = QLineSeries() self.short_bottom_trend_series = QLineSeries() self.long_top_trend_series = QLineSeries() self.long_bottom_trend_series = QLineSeries() self.trend_lines.append(self.short_top_trend_series) self.trend_lines.append(self.short_bottom_trend_series) self.trend_lines.append(self.long_top_trend_series) self.trend_lines.append(self.long_bottom_trend_series) self.chart_view.chart().addSeries(self.candle_stick_series) self.chart_view.chart().addSeries(self.moving_average_series) self.chart_view.chart().addSeries(self.top_edge_series) self.chart_view.chart().addSeries(self.bottom_edge_series) self.chart_view.chart().addSeries(self.short_top_trend_series) self.chart_view.chart().addSeries(self.long_top_trend_series) self.chart_view.chart().addSeries(self.short_bottom_trend_series) self.chart_view.chart().addSeries(self.long_bottom_trend_series) self.chart_view.chart().addAxis(self.price_time_axis, Qt.AlignBottom) self.chart_view.chart().addAxis(self.price_axis, Qt.AlignLeft) self.chart_view.chart().legend().hide() self.chart_view.setRenderHint(QPainter.Antialiasing) self.set_marker_color() self.set_trend_line_pen() def set_trend_line_pen(self): brushes = [ QBrush(QColor(255, 0, 0, 90)), QBrush(QColor(0, 0, 255, 90)), QBrush(QColor(205, 56, 47, 255)), QBrush(QColor(0, 153, 213, 255)) ] for i, tl in enumerate(self.trend_lines): tl.setPen(QPen(brushes[i], 4, Qt.DotLine)) def set_marker_color(self): self.top_edge_series.setPen(Qt.black) self.top_edge_series.setBrush(QBrush(QColor(255, 0, 255, 90))) self.bottom_edge_series.setPen(Qt.black) self.bottom_edge_series.setBrush(QBrush(QColor(0, 255, 255, 90))) def set_datetime(self, d): self.chart_datetime = d self.datetime_range = (d.timestamp() * 1000, d.replace(hour=23, minute=59).timestamp() * 1000) start_time = QDateTime() until_time = QDateTime() start_time.setDate(QDate(d.year, d.month, d.day)) until_time.setDate(QDate(d.year, d.month, d.day)) start_time.setTime(QTime(9, 0)) until_time.setTime(QTime(16, 0)) self.price_time_axis.setRange(start_time, until_time) def attach(self): self.price_time_axis.setTickCount(7) self.candle_stick_series.attachAxis(self.price_time_axis) self.candle_stick_series.attachAxis(self.price_axis) self.moving_average_series.attachAxis(self.price_time_axis) self.moving_average_series.attachAxis(self.price_axis) self.top_edge_series.attachAxis(self.price_time_axis) self.top_edge_series.attachAxis(self.price_axis) self.bottom_edge_series.attachAxis(self.price_time_axis) self.bottom_edge_series.attachAxis(self.price_axis) self.short_top_trend_series.attachAxis(self.price_time_axis) self.short_top_trend_series.attachAxis(self.price_axis) self.long_top_trend_series.attachAxis(self.price_time_axis) self.long_top_trend_series.attachAxis(self.price_axis) self.short_bottom_trend_series.attachAxis(self.price_time_axis) self.short_bottom_trend_series.attachAxis(self.price_axis) self.long_bottom_trend_series.attachAxis(self.price_time_axis) self.long_bottom_trend_series.attachAxis(self.price_axis) def in_datetime_range(self, q): return self.datetime_range[0] < q < self.datetime_range[1] def clear_series_data(self): self.candle_stick_series.clear() self.moving_average_series.clear() self.top_edge_series.clear() self.bottom_edge_series.clear() self.short_top_trend_series.clear() self.long_top_trend_series.clear() self.short_bottom_trend_series.clear() self.long_bottom_trend_series.clear() def get_chart_view(self): return self.chart_view def add_moving_average(self, q, price): if self.in_datetime_range(q): self.moving_average_series.append(q, price) def add_candle_stick(self, q, o, h, l, c): if self.in_datetime_range(q): self.candle_stick_series.append(QCandlestickSet(o, h, l, c, q)) def set_price_range(self, price_min, price_max): self.price_axis.setRange(price_min, price_max) tick_count = int( math.ceil((price_max - price_min) / price_min * 100. / 2.0)) self.price_axis.setTickCount(tick_count if tick_count + 1 > 2 else 2) def add_top_edge(self, q, price): if self.in_datetime_range(q): self.top_edge_series.append(q, price) def add_bottom_edge(self, q, price): if self.in_datetime_range(q): self.bottom_edge_series.append(q, price) def add_short_top_trend(self, q, price, draw_horizontal=False): if self.in_datetime_range(q): if draw_horizontal: self.short_top_trend_series.append(q, price) if self.name == 'yesterday': self.short_top_trend_series.append(self.datetime_range[1], price) else: self.short_top_trend_series.append(self.datetime_range[0], price) else: self.short_top_trend_series.append(q, price) def add_long_top_trend(self, q, price, draw_horizontal=False): if self.in_datetime_range(q): if draw_horizontal: self.long_top_trend_series.append(q, price) if self.name == 'yesterday': self.long_top_trend_series.append(self.datetime_range[1], price) else: self.long_top_trend_series.append(self.datetime_range[0], price) else: self.long_top_trend_series.append(q, price) def add_short_bottom_trend(self, q, price, draw_horizontal=False): if self.in_datetime_range(q): if draw_horizontal: self.short_bottom_trend_series.append(q, price) if self.name == 'yesterday': self.short_bottom_trend_series.append( self.datetime_range[1], price) else: self.short_bottom_trend_series.append( self.datetime_range[0], price) else: self.short_bottom_trend_series.append(q, price) def add_long_bottom_trend(self, q, price, draw_horizontal=False): if self.in_datetime_range(q): if draw_horizontal: self.long_bottom_trend_series.append(q, price) if self.name == 'yesterday': self.long_bottom_trend_series.append( self.datetime_range[1], price) else: self.long_bottom_trend_series.append( self.datetime_range[0], price) else: self.long_bottom_trend_series.append(q, price)
class TelemetryDialog(QDialog): resized = QtCore.pyqtSignal() visibility = QtCore.pyqtSignal(bool) def __init__(self, winTitle="Network Telemetry", parent=None): super(TelemetryDialog, self).__init__(parent) self.visibility.connect(self.onVisibilityChanged) self.winTitle = winTitle self.updateLock = Lock() # Used to detect network change self.lastNetKey = "" self.lastSeen = None self.maxPoints = 20 self.maxRowPoints = 60 self.paused = False self.streamingSave = False self.streamingFile = None self.linesBeforeFlush = 10 self.currentLine = 0 # OK and Cancel buttons #buttons = QDialogButtonBox(QDialogButtonBox.Ok,Qt.Horizontal, self) #buttons.accepted.connect(self.accept) #buttons.move(170, 280) desktopSize = QApplication.desktop().screenGeometry() #self.mainWidth=1024 #self.mainHeight=768 #self.mainWidth = desktopSize.width() * 3 / 4 #self.mainHeight = desktopSize.height() * 3 / 4 self.setGeometry(self.geometry().x(), self.geometry().y(), desktopSize.width() / 2, desktopSize.height() / 2) self.setWindowTitle(winTitle) self.radar = RadarWidget(self) self.radar.setGeometry(self.geometry().width() / 2, 10, self.geometry().width() / 2 - 20, self.geometry().width() / 2 - 20) self.createTable() self.btnExport = QPushButton("Export Table", self) self.btnExport.clicked[bool].connect(self.onExportClicked) self.btnExport.setStyleSheet("background-color: rgba(2,128,192,255);") self.btnPause = QPushButton("Pause Table", self) self.btnPause.setCheckable(True) self.btnPause.clicked[bool].connect(self.onPauseClicked) self.btnPause.setStyleSheet("background-color: rgba(2,128,192,255);") self.btnStream = QPushButton("Streaming Save", self) self.btnStream.setCheckable(True) self.btnStream.clicked[bool].connect(self.onStreamClicked) self.btnStream.setStyleSheet("background-color: rgba(2,128,192,255);") self.createChart() self.setBlackoutColors() self.setMinimumWidth(600) self.setMinimumHeight(600) self.center() def createTable(self): # Set up location table self.locationTable = QTableWidget(self) self.locationTable.setColumnCount(8) self.locationTable.setGeometry(10, 10, self.geometry().width() / 2 - 20, self.geometry().height() / 2) self.locationTable.setShowGrid(True) self.locationTable.setHorizontalHeaderLabels([ 'macAddr', 'SSID', 'Strength', 'Timestamp', 'GPS', 'Latitude', 'Longitude', 'Altitude' ]) self.locationTable.resizeColumnsToContents() self.locationTable.setRowCount(0) self.locationTable.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self.ntRightClickMenu = QMenu(self) newAct = QAction('Copy', self) newAct.setStatusTip('Copy data to clipboard') newAct.triggered.connect(self.onCopy) self.ntRightClickMenu.addAction(newAct) self.locationTable.setContextMenuPolicy(Qt.CustomContextMenu) self.locationTable.customContextMenuRequested.connect( self.showNTContextMenu) def setBlackoutColors(self): self.locationTable.setStyleSheet( "QTableView {background-color: black;gridline-color: white;color: white} QTableCornerButton::section{background-color: white;}" ) headerStyle = "QHeaderView::section{background-color: white;border: 1px solid black;color: black;} QHeaderView::down-arrow,QHeaderView::up-arrow {background: none;}" self.locationTable.horizontalHeader().setStyleSheet(headerStyle) self.locationTable.verticalHeader().setStyleSheet(headerStyle) mainTitleBrush = QBrush(Qt.red) self.timeChart.setTitleBrush(mainTitleBrush) self.timeChart.setBackgroundBrush(QBrush(Qt.black)) self.timeChart.axisX().setLabelsColor(Qt.white) self.timeChart.axisY().setLabelsColor(Qt.white) titleBrush = QBrush(Qt.white) self.timeChart.axisX().setTitleBrush(titleBrush) self.timeChart.axisY().setTitleBrush(titleBrush) def resizeEvent(self, event): wDim = self.geometry().width() / 2 - 20 hDim = self.geometry().height() / 2 smallerDim = wDim if hDim < smallerDim: smallerDim = hDim # Radar self.radar.setGeometry(self.geometry().width() - smallerDim - 10, 10, smallerDim, smallerDim) # chart self.timePlot.setGeometry(10, 10, self.geometry().width() - smallerDim - 30, smallerDim) # Buttons self.btnPause.setGeometry(10, self.geometry().height() / 2 + 18, 110, 25) self.btnExport.setGeometry(150, self.geometry().height() / 2 + 18, 110, 25) self.btnStream.setGeometry(290, self.geometry().height() / 2 + 18, 110, 25) # Table self.locationTable.setGeometry(10, self.geometry().height() / 2 + 50, self.geometry().width() - 20, self.geometry().height() / 2 - 60) def center(self): # Get our geometry qr = self.frameGeometry() # Find the desktop center point cp = QDesktopWidget().availableGeometry().center() # Move our center point to the desktop center point qr.moveCenter(cp) # Move the top-left point of the application window to the top-left point of the qr rectangle, # basically centering the window self.move(qr.topLeft()) def showNTContextMenu(self, pos): curRow = self.locationTable.currentRow() if curRow == -1: return self.ntRightClickMenu.exec_(self.locationTable.mapToGlobal(pos)) def onCopy(self): self.updateLock.acquire() curRow = self.locationTable.currentRow() curCol = self.locationTable.currentColumn() if curRow == -1 or curCol == -1: self.updateLock.release() return curText = self.locationTable.item(curRow, curCol).text() clipboard = QApplication.clipboard() clipboard.setText(curText) self.updateLock.release() def onVisibilityChanged(self, visible): if not visible: self.paused = True self.btnPause.setStyleSheet("background-color: rgba(255,0,0,255);") # We're coming out of streaming self.streamingSave = False self.btnStream.setStyleSheet( "background-color: rgba(2,128,192,255);") self.btnStream.setChecked(False) if (self.streamingFile): self.streamingFile.close() self.streamingFile = None return else: self.paused = False self.btnPause.setStyleSheet( "background-color: rgba(2,128,192,255);") if self.locationTable.rowCount() > 1: self.locationTable.scrollToItem(self.locationTable.item(0, 0)) def hideEvent(self, event): self.visibility.emit(False) def showEvent(self, event): self.visibility.emit(True) def onPauseClicked(self, pressed): if self.btnPause.isChecked(): self.paused = True self.btnPause.setStyleSheet("background-color: rgba(255,0,0,255);") else: self.paused = False self.btnPause.setStyleSheet( "background-color: rgba(2,128,192,255);") def onStreamClicked(self, pressed): if not self.btnStream.isChecked(): # We're coming out of streaming self.streamingSave = False self.btnStream.setStyleSheet( "background-color: rgba(2,128,192,255);") if (self.streamingFile): self.streamingFile.close() self.streamingFile = None return self.btnStream.setStyleSheet("background-color: rgba(255,0,0,255);") self.streamingSave = True fileName = self.saveFileDialog() if not fileName: self.btnStream.setStyleSheet( "background-color: rgba(2,128,192,255);") self.btnStream.setChecked(False) return try: self.streamingFile = open( fileName, 'w', 1 ) # 1 says use line buffering, otherwise it fully buffers and doesn't write except: QMessageBox.question(self, 'Error', "Unable to write to " + fileName, QMessageBox.Ok) self.streamingFile = None self.streamingSave = False self.btnStream.setStyleSheet( "background-color: rgba(2,128,192,255);") self.btnStream.setChecked(False) return self.streamingFile.write( 'MAC Address,SSID,Strength,Timestamp,GPS,Latitude,Longitude,Altitude\n' ) def onExportClicked(self): fileName = self.saveFileDialog() if not fileName: return try: outputFile = open(fileName, 'w') except: QMessageBox.question(self, 'Error', "Unable to write to " + fileName, QMessageBox.Ok) return outputFile.write( 'MAC Address,SSID,Strength,Timestamp,GPS,Latitude,Longitude,Altitude\n' ) numItems = self.locationTable.rowCount() if numItems == 0: outputFile.close() return self.updateLock.acquire() for i in range(0, numItems): outputFile.write( self.locationTable.item(i, 0).text() + ',"' + self.locationTable.item(i, 1).text() + '",' + self.locationTable.item(i, 2).text() + ',' + self.locationTable.item(i, 3).text()) outputFile.write(',' + self.locationTable.item(i, 4).text() + ',' + self.locationTable.item(i, 5).text() + ',' + self.locationTable.item(i, 6).text() + ',' + self.locationTable.item(i, 7).text() + '\n') self.updateLock.release() outputFile.close() def saveFileDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName( self, "QFileDialog.getSaveFileName()", "", "CSV Files (*.csv);;All Files (*)", options=options) if fileName: return fileName else: return None def createChart(self): self.timeChart = QChart() titleFont = QFont() titleFont.setPixelSize(18) titleBrush = QBrush(QColor(0, 0, 255)) self.timeChart.setTitleFont(titleFont) self.timeChart.setTitleBrush(titleBrush) self.timeChart.setTitle('Signal (Past ' + str(self.maxPoints) + ' Samples)') # self.timeChart.addSeries(testseries) # self.timeChart.createDefaultAxes() self.timeChart.legend().hide() # Axis examples: https://doc.qt.io/qt-5/qtcharts-multiaxis-example.html newAxis = QValueAxis() newAxis.setMin(0) newAxis.setMax(self.maxPoints) newAxis.setTickCount(11) newAxis.setLabelFormat("%d") newAxis.setTitleText("Sample") self.timeChart.addAxis(newAxis, Qt.AlignBottom) newAxis = QValueAxis() newAxis.setMin(-100) newAxis.setMax(-10) newAxis.setTickCount(9) newAxis.setLabelFormat("%d") newAxis.setTitleText("dBm") self.timeChart.addAxis(newAxis, Qt.AlignLeft) chartBorder = Qt.darkGray self.timePlot = QChartView(self.timeChart, self) self.timePlot.setBackgroundBrush(chartBorder) self.timePlot.setRenderHint(QPainter.Antialiasing) self.timeSeries = QLineSeries() pen = QPen(Qt.yellow) pen.setWidth(2) self.timeSeries.setPen(pen) self.timeChart.addSeries(self.timeSeries) self.timeSeries.attachAxis(self.timeChart.axisX()) self.timeSeries.attachAxis(self.timeChart.axisY()) def updateNetworkData(self, curNet): if not self.isVisible(): return # Signal is -NN dBm. Need to make it positive for the plot self.radar.updateData(curNet.signal * -1) if self.winTitle == "Client Telemetry": self.setWindowTitle(self.winTitle + " - [" + curNet.macAddr + "] " + curNet.ssid) else: self.setWindowTitle(self.winTitle + " - " + curNet.ssid) self.radar.draw() # Network changed. Clear our table and time data updateChartAndTable = False self.updateLock.acquire() if (curNet.getKey() != self.lastNetKey): self.lastNetKey = curNet.getKey() self.locationTable.setRowCount(0) self.timeSeries.clear() updateChartAndTable = True ssidTitle = curNet.ssid if len(ssidTitle) > 28: ssidTitle = ssidTitle[:28] ssidTitle = ssidTitle + '...' self.timeChart.setTitle(ssidTitle + ' Signal (Past ' + str(self.maxPoints) + ' Samples)') else: if self.lastSeen != curNet.lastSeen: updateChartAndTable = True if updateChartAndTable: # Update chart numPoints = len(self.timeSeries.pointsVector()) if numPoints >= self.maxPoints: self.timeSeries.remove(0) # Now we need to reset the x data to pull the series back counter = 0 for curPoint in self.timeSeries.pointsVector(): self.timeSeries.replace(counter, counter, curPoint.y()) counter += 1 if curNet.signal >= -100: self.timeSeries.append(numPoints, curNet.signal) else: self.timeSeries.append(numPoints, -100) # Update Table self.addTableData(curNet) # Limit points in each if self.locationTable.rowCount() > self.maxRowPoints: self.locationTable.setRowCount(self.maxRowPoints) self.updateLock.release() def addTableData(self, curNet): if self.paused: return # rowPosition = self.locationTable.rowCount() # Always insert at row(0) rowPosition = 0 self.locationTable.insertRow(rowPosition) #if (addedFirstRow): # self.locationTable.setRowCount(1) # ['macAddr','SSID', 'Strength', 'Timestamp','GPS', 'Latitude', 'Longitude', 'Altitude'] self.locationTable.setItem(rowPosition, 0, QTableWidgetItem(curNet.macAddr)) tmpssid = curNet.ssid if (len(tmpssid) == 0): tmpssid = '<Unknown>' newSSID = QTableWidgetItem(tmpssid) self.locationTable.setItem(rowPosition, 1, newSSID) self.locationTable.setItem(rowPosition, 2, IntTableWidgetItem(str(curNet.signal))) self.locationTable.setItem( rowPosition, 3, DateTableWidgetItem(curNet.lastSeen.strftime("%m/%d/%Y %H:%M:%S"))) if curNet.gps.isValid: self.locationTable.setItem(rowPosition, 4, QTableWidgetItem('Yes')) else: self.locationTable.setItem(rowPosition, 4, QTableWidgetItem('No')) self.locationTable.setItem( rowPosition, 5, FloatTableWidgetItem(str(curNet.gps.latitude))) self.locationTable.setItem( rowPosition, 6, FloatTableWidgetItem(str(curNet.gps.longitude))) self.locationTable.setItem( rowPosition, 7, FloatTableWidgetItem(str(curNet.gps.altitude))) #order = Qt.DescendingOrder #self.locationTable.sortItems(3, order ) # If we're in streaming mode, write the data out to disk as well if self.streamingFile: self.streamingFile.write( self.locationTable.item(rowPosition, 0).text() + ',"' + self.locationTable.item(rowPosition, 1).text() + '",' + self.locationTable.item(rowPosition, 2).text() + ',' + self.locationTable.item(rowPosition, 3).text() + ',' + self.locationTable.item(rowPosition, 4).text() + ',' + self.locationTable.item(rowPosition, 5).text() + ',' + self.locationTable.item(rowPosition, 6).text() + ',' + self.locationTable.item(rowPosition, 7).text() + '\n') if (self.currentLine > self.linesBeforeFlush): self.streamingFile.flush() self.currentLine += 1 numRows = self.locationTable.rowCount() if numRows > 1: self.locationTable.scrollToItem(self.locationTable.item(0, 0)) def onTableHeadingClicked(self, logical_index): header = self.locationTable.horizontalHeader() order = Qt.DescendingOrder # order = Qt.DescendingOrder if not header.isSortIndicatorShown(): header.setSortIndicatorShown(True) elif header.sortIndicatorSection() == logical_index: # apparently, the sort order on the header is already switched # when the section was clicked, so there is no need to reverse it order = header.sortIndicatorOrder() header.setSortIndicator(logical_index, order) self.locationTable.sortItems(logical_index, order) def updateData(self, newRadius): self.radar.updateData(newRadius) def showTelemetry(parent=None): dialog = TelemetryDialog(parent) result = dialog.exec_() return (result == QDialog.Accepted)
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 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 MyWidget(QWidget): def __init__(self): super().__init__() uic.loadUi('ws_ui.ui', self) self.con = sqlite3.connect("ws_database.db") self.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.create_linechart() self.tmp, self.hmd, self.prs = 0, 0, 0 self.make_measure() self.measure_timer = QTimer(self) self.measure_timer.setInterval(MEASURE_FREQUENCIES * 1000) self.measure_timer.timeout.connect(self.make_measure) self.measure_timer.start() self.update_timer = QTimer(self) self.update_timer.setInterval(1000) self.update_timer.timeout.connect(self.update_labels) self.update_timer.start() self.update_labels() def quit(self): self.destroy() quit() def make_measure(self): sns = sensor_measure() if sns[0] != ERROR_CODE: self.tmp = sns[0] else: print('tmp error') if sns[1] != ERROR_CODE: self.hmd = sns[1] else: print('hmd error') if sns[2] != ERROR_CODE: self.prs = sns[2] else: print('prs error') time = int(dt.datetime.now().timestamp()) req = """ INSERT INTO short_term_data(tmp, hmd, prs, time_from_epoch) VALUES(?,?,?,?) """ self.con.execute(req, (self.tmp, self.hmd, self.prs, time)) self.con.commit() self.update_linechart() def update_labels(self): deg = u'\N{DEGREE SIGN}' hpa = 'ʰᴾᵃ' self.time_label.setText(dt.datetime.now().strftime('%H:%M')) self.tmp_label.setText('{} {}C'.format(self.tmp, deg)) self.hmd_label.setText('{} %'.format(self.hmd)) self.prs_label.setText('{} {}'.format(self.prs, hpa)) def create_linechart(self): self.chart = QChart() self.chart.legend().hide() self.series = QLineSeries() self.axisValue = QValueAxis() self.axisCurrentTime = QValueAxis() self.axisTime = QDateTimeAxis() self.axisTime.setFormat("hh:mm") self.chartview = QChartView(self.chart, self.groupBox) self.chartview.resize(540, 460) self.chartview.move(0, 0) self.chartview.setRenderHint(QPainter.Antialiasing) def update_linechart(self): if self.axisTime in self.chart.axes(): self.chart.removeAxis(self.axisTime) if self.axisCurrentTime in self.chart.axes(): self.chart.removeAxis(self.axisCurrentTime) if self.axisValue in self.chart.axes(): self.chart.removeAxis(self.axisValue) if self.series in self.chart.series(): self.chart.removeSeries(self.series) self.series.clear() self.axisValue.setMax(50) self.axisValue.setMin(-50) req = """ SELECT tmp, time_from_epoch FROM short_term_data WHERE (time_from_epoch - ?) < 86400 AND NOT tmp = ? """ cur = self.con.cursor() result = list(cur.execute(req, (int(dt.datetime.now().timestamp()), ERROR_CODE))) for measure in result: self.series.append(measure[1] * 1000, measure[0]) self.axisTime.setMin(QDateTime.fromMSecsSinceEpoch(int(dt.datetime.now().timestamp()) * 1000 - 86390000)) self.axisTime.setMax(QDateTime.fromMSecsSinceEpoch(int(dt.datetime.now().timestamp()) * 1000)) self.chart.addSeries(self.series) self.chart.addAxis(self.axisTime, Qt.AlignBottom) self.series.attachAxis(self.axisTime) self.chart.addAxis(self.axisValue, Qt.AlignLeft) self.series.attachAxis(self.axisValue) self.chart.setTitle('Температура')