def set_series(self, series: QLineSeries): self.chart().removeAllSeries() if not series or len(series) == 0: return self.chart().addSeries(series) series.setPointLabelsClipping(False) series.hovered.connect(lambda p, s: series.setPointLabelsVisible(s)) x_y_points = list(zip(*((e.x(), e.y()) for e in series.pointsVector()))) axisX = self.chart().axisX() axisX.setRange(min(x_y_points[0]), max(x_y_points[0])) axisX.setMinorTickCount(1) axisX.setTickCount(10) axisX.applyNiceNumbers() self.chart().setAxisX(axisX, series) axisY = self.chart().axisY() axisY.setRange(min(x_y_points[1]), max(x_y_points[1])) axisY.setTickCount(18) axisY.applyNiceNumbers() self.chart().setAxisY(axisY, series)
def update_range(self, series: QLineSeries): x_y_points = list(zip(*((e.x(), e.y()) for e in series.pointsVector()))) self.x_range = min(x_y_points[0]), max(x_y_points[0]) self.chart().axisX().setRange(*self.x_range) if not self.y_range_fixed: self.y_range = min(x_y_points[1]), max(x_y_points[1]) self.chart().axisY().setRange(*self.y_range)
class ChartWidget(QWidget): def __init__(self, parent=None, ticker="BTC"): super().__init__(parent) uic.loadUi("chart.ui", self) self.ticker = ticker self.viewLimit = 128 self.priceData = QLineSeries() self.priceChart = QChart() self.priceChart.addSeries(self.priceData) self.priceChart.legend().hide() axisX = QDateTimeAxis() axisX.setFormat("hh:mm:ss") axisX.setTickCount(4) dt = QDateTime.currentDateTime() axisX.setRange(dt, dt.addSecs(self.viewLimit)) axisY = QValueAxis() axisY.setVisible(False) self.priceChart.addAxis(axisX, Qt.AlignBottom) self.priceChart.addAxis(axisY, Qt.AlignRight) self.priceData.attachAxis(axisX) self.priceData.attachAxis(axisY) self.priceChart.layout().setContentsMargins(0, 0, 0, 0) self.priceView.setChart(self.priceChart) self.priceView.setRenderHints(QPainter.Antialiasing) # ----------------- 추 가 ------------------ self.pw = PriceWorker(ticker) self.pw.dataSent.connect(self.appendData) self.pw.start() # ------------------------------------------ def appendData(self, currPirce): if len(self.priceData) == self.viewLimit: self.priceData.remove(0) dt = QDateTime.currentDateTime() self.priceData.append(dt.toMSecsSinceEpoch(), currPirce) self.__updateAxis() def __updateAxis(self): pvs = self.priceData.pointsVector() dtStart = QDateTime.fromMSecsSinceEpoch(int(pvs[0].x())) if len(self.priceData) == self.viewLimit: dtLast = QDateTime.fromMSecsSinceEpoch(int(pvs[-1].x())) else: dtLast = dtStart.addSecs(self.viewLimit) ax = self.priceChart.axisX() ax.setRange(dtStart, dtLast) ay = self.priceChart.axisY() dataY = [v.y() for v in pvs] ay.setRange(min(dataY), max(dataY))
class PeersCountGraphView(BaseTimedGraph): peersCountFetched = AsyncSignal(int) def __init__(self, parent=None): super(PeersCountGraphView, self).__init__(parent=parent) self.setObjectName('PeersCountGraph') self.peersCountFetched.connectTo(self.onPeersCount) self.axisY.setTitleText(iPeers()) pen = QPen(QColor('#60cccf')) pen.setWidth(2) self.seriesPeers = QLineSeries() self.seriesPeers.setName('Peers') self.seriesPeers.setPen(pen) self.chart.addSeries(self.seriesPeers) self.seriesPeers.attachAxis(self.axisX) self.seriesPeers.attachAxis(self.axisY) self.setChart(self.chart) async def onPeersCount(self, count): dtNow = QDateTime.currentDateTime() dtMsecs = dtNow.toMSecsSinceEpoch() delta = 120 * 1000 if not self.seriesPurgedT or (dtMsecs - self.seriesPurgedT) > delta: self.removeOldSeries(self.seriesPeers, dtMsecs - delta) self.seriesPurgedT = dtMsecs peersVector = self.seriesPeers.pointsVector() values = [p.y() for p in peersVector] if values: self.axisY.setMax(max(values) + 10) self.axisY.setMin(min(values) - 10) dateMin = QDateTime.fromMSecsSinceEpoch(dtMsecs - (30 * 1000)) dateMax = QDateTime.fromMSecsSinceEpoch(dtMsecs + 2000) self.seriesPeers.append(dtMsecs, count) self.axisX.setRange(dateMin, dateMax)
class MyWindow(QMainWindow): def __init__(self): super().__init__() # thread self.worker = Worker() self.worker.price.connect(self.get_price) self.worker.start() # window size self.setMinimumSize(600, 400) # data self.series = QLineSeries() # chart object self.chart = QChart() self.chart.legend().hide() self.chart.addSeries(self.series) # data feeding # axis axis_x = QDateTimeAxis() axis_x.setFormat("hh:mm:ss") dt = QDateTime.currentDateTime() axis_x.setRange(dt, dt.addSecs(128)) self.chart.addAxis(axis_x, Qt.AlignBottom) self.series.attachAxis(axis_x) axis_y = QValueAxis() axis_y.setLabelFormat("%i") self.chart.addAxis(axis_y, Qt.AlignLeft) self.series.attachAxis(axis_y) # margin self.chart.layout().setContentsMargins(0, 0, 0, 0) # displaying chart chart_view = QChartView(self.chart) chart_view.setRenderHint(QPainter.Antialiasing) self.setCentralWidget(chart_view) @pyqtSlot(float) def get_price(self, cur_price): if len(self.series) == 128: self.series.remove(0) # delete first data # append current price dt = QDateTime.currentDateTime() ts = dt.toMSecsSinceEpoch() self.series.append(ts, cur_price) print(ts, cur_price) # update asis data = self.series.pointsVector() first_ts = data[0].x() last_ts = data[-1].x() first_dt = QDateTime.fromMSecsSinceEpoch(first_ts) last_dt = QDateTime.fromMSecsSinceEpoch(last_ts) axis_x = self.chart.axisX() axis_x.setRange(first_dt, last_dt) axis_y = self.chart.axisY() axis_y.setRange(70000000, 71000000)
class BandwidthGraphView(BaseTimedGraph): bwStatsFetched = AsyncSignal(dict) def __init__(self, parent=None): super(BandwidthGraphView, self).__init__(parent=parent) self.setObjectName('BandwidthGraph') self.chart.setTitle(iBandwidthUsage()) self.axisY.setTitleText(iRateKbPerSecond()) self.bwStatsFetched.connectTo(self.onBwStats) penIn = QPen(QColor('red')) penIn.setWidth(2) penOut = QPen(QColor('blue')) penOut.setWidth(2) self.seriesKbIn = QLineSeries() self.seriesKbIn.setName(iRateInput()) self.seriesKbIn.setPen(penIn) self.chart.addSeries(self.seriesKbIn) self.seriesKbIn.attachAxis(self.axisX) self.seriesKbIn.attachAxis(self.axisY) self.seriesKbOut = QLineSeries() self.seriesKbOut.setName(iRateOutput()) self.seriesKbOut.setPen(penOut) self.chart.addSeries(self.seriesKbOut) self.seriesKbOut.attachAxis(self.axisX) self.seriesKbOut.attachAxis(self.axisY) self.setChart(self.chart) async def onBwStats(self, stats): dtNow = QDateTime.currentDateTime() dtMsecs = dtNow.toMSecsSinceEpoch() delta = 120 * 1000 if not self.seriesPurgedT or (dtMsecs - self.seriesPurgedT) > delta: self.removeOldSeries(self.seriesKbIn, dtMsecs - delta) self.removeOldSeries(self.seriesKbOut, dtMsecs - delta) self.seriesPurgedT = dtMsecs rateIn = stats.get('RateIn', 0) rateOut = stats.get('RateOut', 0) rateInKb = int(rateIn / 1024) rateOutKb = int(rateOut / 1024) inVector = self.seriesKbIn.pointsVector() outVector = self.seriesKbIn.pointsVector() inValues = [p.y() for p in inVector] outValues = [p.y() for p in outVector] if inValues or outValues: self.axisY.setMax(max(max(inValues), max(outValues))) dateMin = QDateTime.fromMSecsSinceEpoch(dtMsecs - (30 * 1000)) dateMax = QDateTime.fromMSecsSinceEpoch(dtMsecs + 2000) self.seriesKbIn.append(dtMsecs, rateInKb) self.seriesKbOut.append(dtMsecs, rateOutKb) self.axisX.setRange(dateMin, dateMax)
class PulseGen(QtWidgets.QMainWindow, Ui_MainWindow): GENERATOR_PORT = 5000 def __init__(self, parent=None): super(PulseGen, self).__init__(parent) self.setupUi(self) self.pulse_gen_display = PulseGenDisplay() self.display_data = [0] * 500 self.pulse_height_max = self.pulse_height_dial.maximum() self.pulse_height_min = self.pulse_height_dial.minimum() self.pulse_gen_display.pulse_height = self.pulse_height_max # set pulse height self.pulse_gen_display.pulse_height = self.pulse_height_max self.pulse_height_dial.setValue(self.pulse_gen_display.pulse_height) self.pulse_height_dial.actionTriggered.connect(self.avoid_wrapping) self.pulse_height_dial.valueChanged.connect(self.pulse_height_changed) self.pulse_height_text.setText(str( self.pulse_gen_display.pulse_height)) # don't allow editing self.pulse_height_text.textChanged.connect(self.no_editing) # pulse shape for pulse_form in self.pulse_gen_display.pulse_shape.keys(): self.pulse_shape_combo.addItem(pulse_form) self.pulse_shape_combo.currentIndexChanged.connect( self.pulse_shape_changed) current_pulse_form = self.pulse_shape_combo.currentText() self.pulse_gen_display.pulse_form = self.pulse_gen_display.pulse_shape[ current_pulse_form] print("Current pulse form: " + current_pulse_form + " code: " + str(self.pulse_gen_display.pulse_form)) for pulse_freq in self.pulse_gen_display.frequencies.keys(): self.gen_freq_combo.addItem(pulse_freq) current_pulse_freq = self.gen_freq_combo.currentText() self.pulse_gen_display.pulse_T = self.pulse_gen_display.frequencies[ current_pulse_freq] print("Current pulse T: " + str(self.pulse_gen_display.pulse_T)) self.gen_freq_combo.currentIndexChanged.connect( self.pulse_freq_changed) for display_freq in self.pulse_gen_display.frequencies.keys(): self.display_freq_combo.addItem(display_freq) self.display_freq_combo.currentIndexChanged.connect( self.display_freq_changed) current_display_freq = self.display_freq_combo.currentText() self.pulse_gen_display.display_T = self.pulse_gen_display.frequencies[ current_display_freq] print("Current display T: " + str(self.pulse_gen_display.display_T)) self.start_stop_pb.clicked.connect(self.start_stop_meas) self.connect_pb.clicked.connect(self.connect) self.gen_chart = QChart() self.gen_chart.legend().hide() self.x_axis = QValueAxis() self.x_axis.setMax(500 * self.pulse_gen_display.display_T) self.x_axis.setMin(0) self.y_axis = QValueAxis() self.y_axis.setMax(3.3) self.y_axis.setMin(0) self.gen_chart.addAxis(self.x_axis, Qt.AlignBottom) self.gen_chart.addAxis(self.y_axis, Qt.AlignLeft) self.generator_screen.setChart(self.gen_chart) # display the initial pulse self.pulse_gen_display.calc_pulse() self.pulse_gen_display.get_display_data() self.series = QLineSeries() for i in range(500): self.series.append( i * self.pulse_gen_display.display_T, self.pulse_gen_display.display_data[i] * self.pulse_gen_display.calib) self.gen_chart.addSeries(self.series) self.series.attachAxis(self.x_axis) self.series.attachAxis(self.y_axis) self.display_pulse() self.pulse_height_timer = QTimer() self.pulse_height_timer.setSingleShot(True) self.pulse_height_timer.timeout.connect(self.pulse_height_ready) self.genRunning = False self.actionQuit.triggered.connect(self.quit) self.client_socket = QTcpSocket(self) self.connected = False self.show() def no_editing(self): self.pulse_height_text.setText(str( self.pulse_gen_display.pulse_height)) def avoid_wrapping(self, val): if val == QtWidgets.QAbstractSlider.SliderMove: minDistance = 1 if self.pulse_height_dial.value() == self.pulse_height_max \ and self.pulse_height_dial.sliderPosition()<self.pulse_height_max-minDistance: self.pulse_height_dial.setSliderPosition(self.pulse_height_max) elif self.pulse_height_dial.value() == self.pulse_height_min \ and self.pulse_height_dial.sliderPosition()>self.pulse_height_min+minDistance: self.pulse_height_dial.setSliderPosition(self.pulse_height_min) def pulse_shape_changed(self): ''' if self.genRunning: print("Current shape index: ",self.pulse_shape_combo.currentIndex()) print("current pulse form: ",self.pulse_gen_display.pulse_form) if self.pulse_shape_combo.currentIndex() == self.pulse_gen_display.pulse_form: return QMessageBox.warning(self, 'Pulse generation is active', 'Pulse generator is running\nPlease stop pulse generation before changing parameters') self.pulse_shape_combo.setCurrentIndex(self.pulse_gen_display.pulse_form) return ''' print("pulse shape changed") current_pulse_form = self.pulse_shape_combo.currentText() self.pulse_gen_display.pulse_form = self.pulse_gen_display.pulse_shape[ current_pulse_form] print("Current pulse form: " + current_pulse_form + " code: " + str(self.pulse_gen_display.pulse_form)) self.display_pulse() if not self.connected: return else: pulse_form_msg = "pulse_shape=" + str( self.pulse_gen_display.pulse_form) + "\n" print("Sending: " + pulse_form_msg) self.client_socket.write(bytes(pulse_form_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) def pulse_freq_changed(self): new_freq = self.gen_freq_combo.currentText() self.pulse_gen_display.pulse_T = self.pulse_gen_display.frequencies[ new_freq] print("Pulse frequency changed to " + new_freq + "T: " + str(self.pulse_gen_display.pulse_T)) self.display_pulse() ''' if self.genRunning: if self.pulse_gen_display.frequencies[self.gen_freq_combo.currentText()] == self.pulse_gen_display.pulse_T: return QMessageBox.warning(self, 'Pulse generation is active', 'Pulse generator is running\nPlease stop pulse generation before changing parameters') index = 0 for freq in self.pulse_gen_display.frequencies.keys(): print("freq: ",freq) print("val: ",self.pulse_gen_display.frequencies[freq]) if self.pulse_gen_display.frequencies[freq] == self.pulse_gen_display.pulse_T: self.gen_freq_combo.setCurrentIndex(index) break index += 1 return ''' if not self.connected: return else: pulse_T_msg = "pulse_T=" + str( self.pulse_gen_display.pulse_T) + "\n" print("Sending: " + pulse_T_msg) self.client_socket.write(bytes(pulse_T_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) def display_freq_changed(self): new_freq = self.display_freq_combo.currentText() self.pulse_gen_display.display_T = self.pulse_gen_display.frequencies[ new_freq] self.display_pulse() def pulse_height_changed(self): self.pulse_gen_display.pulse_height = self.pulse_height_dial.value() self.pulse_height_text.setText(str( self.pulse_gen_display.pulse_height)) self.display_pulse() self.pulse_height_timer.start(500) def pulse_height_ready(self): print("Timeout") if not self.connected: return else: pulse_height_msg = "pulse_height=" + str( self.pulse_gen_display.pulse_height) + "\n" print("Sending: " + pulse_height_msg) self.client_socket.write(bytes(pulse_height_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) def connect(self): if self.connected: self.client_socket.close() self.connected = False self.connect_pb.setChecked(False) self.connect_pb.setText("Connect to Pulse Generator") return server_ip = str(self.server_ip_text.text()) port = self.GENERATOR_PORT print("Server IP: ", server_ip) if server_ip.find('xxx') != -1: print("bad IP") QMessageBox.about( self, 'Bad Server IP', 'Please give a correct Server IP\n' 'IP is ' + server_ip) self.connect_pb.setChecked(False) return else: print("Connecting to " + server_ip + ":", port) self.client_socket.connectToHost(server_ip, port) self.client_socket.waitForConnected(1000) if self.client_socket.state() != QTcpSocket.ConnectedState: QMessageBox.warning( self, 'Connection failed', 'Please check IP address and port number\nIs the server running?' ) self.connect_pb.setChecked(False) return print("Connection established") self.connect_pb.setText("Connected") self.client_socket.waitForReadyRead() connectMsg = self.client_socket.readAll() msgstring = bytes(connectMsg).decode() print("Connection message:" + msgstring) print("Send generator settings") pulse_form_msg = "pulse_shape=" + str( self.pulse_gen_display.pulse_form) + "\n" print("Sending: " + pulse_form_msg) self.client_socket.write(bytes(pulse_form_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) pulse_T_msg = "pulse_T=" + str(self.pulse_gen_display.pulse_T) + "\n" print("Sending: " + pulse_T_msg) self.client_socket.write(bytes(pulse_T_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) pulse_height_msg = "pulse_height=" + str( self.pulse_gen_display.pulse_height) + "\n" print("Sending: " + pulse_height_msg) self.client_socket.write(bytes(pulse_height_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) running_msg = 'running?=\n' print("Sending: " + running_msg) self.client_socket.write(bytes(running_msg, encoding="ascii")) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() response = bytes(responseMsg).decode() print("reponse msg: ", response) if response == 'yes\n': print("task is running") elif response == 'no\n': print("task is not running") else: print("Don't know if task is running") self.connected = True def start_stop_meas(self): if not self.connected: QMessageBox.about( self, 'You must be connected to the generator server\n' 'please connect and try again') return if self.start_stop_pb.isChecked(): print("Start generation") # send current generator settings self.start_stop_pb.setText("Stop Pulse Generation") msg = 'start=\n' print("Sending: " + msg) self.client_socket.write(msg.encode()) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) self.genRunning = True else: print("Stop generation") self.start_stop_pb.setText("Start Pulse Generation") msg = 'stop=\n' self.client_socket.write(msg.encode()) self.client_socket.waitForReadyRead() responseMsg = self.client_socket.readAll() print("reponse msg: ", bytes(responseMsg).decode()) self.genRunning = False def readTrace(self): print("Message from the generator") data = self.server_socket.readAll() msgstring = bytes(data).decode() print(msgstring) print(data, type(data)) def display_pulse(self): self.pulse_gen_display.calc_pulse() self.pulse_gen_display.get_display_data() self.x_axis.setMax(500 * self.pulse_gen_display.display_T) data_points = self.series.pointsVector() for i in range(500): data_points[i].setX(i * self.pulse_gen_display.display_T) data_points[i].setY(self.pulse_gen_display.display_data[i] * self.pulse_gen_display.calib) self.series.replace(data_points) def quit(self): app.exit()
class 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 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 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 Chart(QChart): MIN_X = 0 MAX_X = 750 MIN_Y = -1 MAX_Y = 1 TICKS = 5 PENCOLOR = Qt.red PENWIDTH = 1 def __init__(self, parent=None): super(Chart, self).__init__(parent) self.parent = parent # we will draw lines self.series = QLineSeries(parent) # color and pen-width self.series.setPen(QPen(self.PENCOLOR, self.PENWIDTH)) self.addSeries(self.series) self.legend().hide() self.__construct_axises() def setRange_X_axis(self, min_X, max_X): self.MIN_X = min_X self.MAX_X = max_X self.axisX().setRange(self.MIN_X, self.MAX_X) def setRange_Y_axis(self, min_Y, max_Y): self.MIN_Y = min_Y self.MAX_Y = max_Y self.axisY().setRange(self.MIN_Y, self.MAX_Y) def add_point(self, x, y): self.series.append(x, y) def get_series(self) -> list: return self.series.pointsVector() def remove_point(self, index): self.series.remove(index) def remove_points(self, index, count): self.series.removePoints(index, count) def replace_point(self, index, x, y): self.series.replace(index, x, y) def replace_series(self, lst): self.series.replace(lst) def get_series_count(self): return self.series.count() def __construct_axises(self): self.createDefaultAxes() # X-Axis x_axis = self.axisX() x_axis.hide() x_axis.setRange(self.MIN_X, self.MAX_X) # Y-axis y_axis = self.axisY() y_axis.setRange(self.MIN_Y, self.MAX_Y) y_axis.setTickCount(self.TICKS)
class MainView(QWidget, Ui_Dialog): def __init__(self): super().__init__() Ui_Dialog.setupUi(self, self) self.pushButton_3.clicked.connect(self.showRecords) self.pushButton.clicked.connect(self.start) self.recive = False self.s = socket.socket() # Create a socket object self.c = socket.socket() # Create a socket object host = "192.168.1.40" # Get local machine name port = 12345 # Reserve a port for your serv host = "" # Get local machine nameice. self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.c = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.c.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.pushButton_3.setDisabled(True) self.pushButton.setText("Stop") try: self.s.bind(("", port)) # Bind to the port except: print("error") # self.s.listen(5) # Now wait for client connection. def __del__(self): self.s.close() def showConfiguration(self): self.hide() self.conf = configureView.configureView(self) self.conf.setMain(self) self.conf.show() def showRecords(self): self.w = QWidget() self.w.show() self.hide() self.gridLayout = QtWidgets.QVBoxLayout(self.w) self.text = QtWidgets.QLabel(self.w) self.chart = QChart() self.chart2 = QChart() self.chart3 = QChart() self.text.setText("none") self.backButton = QtWidgets.QPushButton(self.w) self.chartView = QChartView(self.chart) self.chartView.setRenderHint(QtGui.QPainter.Antialiasing) # self.chartView2.setRenderHint(QtGui.QPainter.Antialiasing) # self.chartView3.setRenderHint(QtGui.QPainter.Antialiasing) self.gridLayout.addWidget(self.chartView) # self.gridLayout.addWidget(self.chartView3) self.backButton.setText("Back") self.gridLayout.addWidget(self.backButton) self.backButton.clicked.connect(self.back) self.recive = True # self.series2 = QLineSeries(self.chart2) # self.series3 = QLineSeries(self.chart3) self.series = QLineSeries(self.chart) self.series2 = QLineSeries(self.chart) self.series3 = QLineSeries(self.chart) # self.series2.setUseOpenGL(True) # self.series3.setUseOpenGL(True) self.series.setUseOpenGL(True) self.series2.setUseOpenGL(True) self.series3.setUseOpenGL(True) # self.chart.addSeries(self.series) self.startServer() def createLineChart(self, dataTable): self.chart.setTitle("Sensor1") series = QLineSeries(self.chart) series2 = QLineSeries(self.chart) series3 = QLineSeries(self.chart) # series2 = QLineSeries(self.chart) # series3 = QLineSeries(self.chart) series.setUseOpenGL(True) series2.setUseOpenGL(True) series3.setUseOpenGL(True) # series2.setUseOpenGL(True) # series3.setUseOpenGL(True) series.replace(self.series.pointsVector()) series2.replace(self.series2.pointsVector()) series3.replace(self.series3.pointsVector()) # series2.replace(self.series2.pointsVector()) # series3.replace(self.series2.pointsVector()) self.chart.removeAllSeries() self.chart.addSeries(series2) self.chart.addSeries(series) self.chart.addSeries(series3) # self.chart2.removeAllSeries() # self.chart2.addSeries(series2) # self.chart3.removeAllSeries() # self.chart3.addSeries(series3) # self.chart.scroll(1,2) def startServer(self): # Establish connection with client. a = [] QApplication.processEvents() self.c.connect(("192.168.1.41", 12346)) self.c.send(b'RecordView True') print("connected") index = 0 while self.recive: QApplication.processEvents() # c, addr = self.s.accept() message, adre = self.s.recvfrom(1024) # print(message) values = message.decode("utf-8").split("-") value1 = int(values[0]) value2 = int(values[1]) value3 = int(values[2]) self.series.append(index, value1) self.series2.append(index, value2) self.series3.append(index, value3) # self.series2.append(index, int(message.decode("utf-8"))) # self.series3.append(index, int(message.decode("utf-8"))) if self.series.count() > 100: self.series.removePoints(0, 1) self.series2.removePoints(0, 1) self.series3.removePoints(0, 1) # self.series2.removePoints(0,1) # self.series3.removePoints(0,1) index += 1 a.append(message.decode("utf-8")) self.createLineChart(a) self.c.connect(("192.168.1.41", 12346)) self.c.send(b'RecordView False') def back(self): self.w.hide() self.show() self.recive = False def configure(self, arguments): print(arguments) self.c.connect(("192.168.1.41", 12346)) self.c.send(b'Sensor ' + arguments) def start(self): self.c.connect(("192.168.1.41", 12346)) if self.pushButton.text() == "Start": text = "Stop" self.pushButton_3.setEnabled(False) self.c.send(b'Record False') else: text = "Start" self.pushButton_3.setEnabled(True) self.c.send(b'Record True') self.recive = self.pushButton.text() == "Start" self.pushButton.setText(text)
class ChartWidget(QWidget): def __init__(self, parent=None, ticker="BTC"): super().__init__(parent) # chart.ui 파일을 읽어와서 디자인을 적용한다. uic.loadUi("resource/chart.ui", self) self.ticker = ticker self.viewLimit = 120 # 라인 차트로 그릴 데이터의 수를 미리 정의한다. self.priceData = QLineSeries() self.priceChart = QChart() self.priceChart.addSeries(self.priceData) self.priceChart.legend().hide() # 차트의 범례를 숨긴다. axisX = QDateTimeAxis() # PyChart에서 날짜 축을 관리하는 QDateTimeAxis 객체를 생성한다. axisX.setFormat("hh:mm:ss") # 시:분:초 형태로 차트에 표시한다. axisX.setTickCount(4) # 차트에 표시할 날짜의 개수를 4로 지정한다. dt = QDateTime.currentDateTime() # 현재 시간 정보를 QDateTime 객체로 얻어온다. # X축에 출력될 값의 범위를 현재 시간부터 viewLimit(120)초 이후까지 설정한다. # addSecs 메서드는 지정된 초 이후의 시간을 QDateTime으로 반환한다. axisX.setRange(dt, dt.addSecs(self.viewLimit)) axisY = QValueAxis() # 정수를 저장하는 축을 생성하고 축의 레이블을 차트에 표시하지 않는다. axisY.setVisible(False) self.priceChart.addAxis(axisX, Qt.AlignBottom) self.priceChart.addAxis(axisY, Qt.AlignRight) self.priceData.attachAxis(axisX) self.priceData.attachAxis(axisY) # 차트 객체 안에 여백을 최소화해서 차트를 크게 그린다. self.priceChart.layout().setContentsMargins(0, 0, 0, 0) self.priceView.setChart(self.priceChart) self.priceView.setRenderHints( QPainter.Antialiasing) # 차트에 anti-aliasing을 적용한다. # PriceWorker 객체 생성 및 dataSent 이벤트를 연결할 슬롯을 지정한다. self.pw = PriceWorker(ticker) self.pw.dataSent.connect(self.appendData) self.pw.start() # 차트에 그릴 데이터를 입력받는다. def appendData(self, currPrice): # 정해진 데이터 개수만큼 저장되어 있다면 오래된 0번 인덱스의 데이터를 삭제한다. # 삭제 로직이 없다면 저장되는 데이터의 개수가 무한히 증가할 것이다. if len(self.priceData) == self.viewLimit: self.priceData.remove(0) dt = QDateTime.currentDateTime() # append 메서드는 millisecond(ms)를 입력받기 때문에 MSecsSinceEpoch() 메서드로 QDateTime 객체를 millisecond로 변환한다. self.priceData.append(dt.toMSecsSinceEpoch(), currPrice) # 차트의 축 정보를 업데이트 한다. 실시간으로 추가되는 데이터의 위치를 지정한다. self.__updateAxis() def __updateAxis(self): # QLineSerires 객체에 저장된 데이터를 리스트로 얻어온다. # pvs에 저장된 리스트 안에는 QPointF 객체로 위치 정보가 저장되어 있다. pvs = self.priceData.pointsVector() # 가장 오래된 0번 인덱스의 객체를 하나 선택해서 x 좌표에 저장된 값을 가져온다. # ms로 변환해서 들어간 좌표 데이터를 fromMSecsSinceEpoch 메서드를 사용해서 QDateTime 객체로 변환한다. dtStart = QDateTime.fromMSecsSinceEpoch(int(pvs[0].x())) # 데이터가 꽉 차 있다면 최근 시간 정보가 들어 있는 마지막 객체를 선택한다. if len(self.priceData) == self.viewLimit: dtLast = QDateTime.fromMSecsSinceEpoch(int(pvs[-1].x())) # 데이터가 꽉 차 있지 않다면 시작 위치를 기준으로 viewLimit 초 이후까지 출력한다. # 항상 viewLimit 개의 데이터를 출력하는데 사용된다. else: dtLast = dtStart.addSecs(self.viewLimit) # 앞서 얻어온 위치 정보를 보여줄 수 있도록 X 축의 범위를 설정한다. ax = self.priceChart.axisX() ax.setRange(dtStart, dtLast) # QPointF 객체에서 y 좌표를 가져와서 최소값, 최대값으로 Y축에 표시될 범위를 지정한다. ay = self.priceChart.axisY() dataY = [v.y() for v in pvs] ay.setRange(min(dataY), max(dataY)) # QWidget에 정의된 메서드로 UI의 종료 버튼을 누르면 실행된다. # 자식 클래스에서 closeEvent를 재정의해서 종료되기 전 쓰레드를 종료한다. def closeEvent(self, event): self.pw.close()
class ChartWidget(QWidget ): #추후 메인 GUI에 추가할 목적이므로 QWidget 클래스를 상속 ChartWidget클래스를 정의 def __init__( self, parent=None, ticker="KRW-ETH" ): #파라미터 parent는 위젯이 그려질 위치를 지정하는데 사용, 입력하지 않으면 None #티커는 조회할 코인의 종류를 지정 super().__init__(parent) uic.loadUi("source/chart.ui", self) self.ticker = ticker self.viewLimit = 128 #라인 차트로 그릴 데이터의 수를 미리 정의 self.pw = PriceWorker(ticker) self.pw.dataSent.connect(self.appendData) self.pw.start() self.priceData = QLineSeries( ) #QLineSeries 객체의 append메서드로 출력할 데이터의 좌표를 x, y 순서대로 입력 self.priceChart = QChart() #데이터를 차트 객체로 전달해서 시각화 #QChart를 사용해 차트의 타이틀을 입력하거나 범례를 추가하는 등의 일을 할 수 있음 self.priceChart.addSeries(self.priceData) self.priceChart.legend().hide() #차트의 범례를 숨김 axisX = QDateTimeAxis() #PyChart에서 날짜 축을 관리하는 QDateTimeAxis 객체를 생성 axisX.setFormat("hh:mm:ss") #"시:분:초" 형태로 차트에 표시 axisX.setTickCount(4) #표시할 날짜의 개수를 4로 지정 dt = QDateTime.currentDateTime() #현재 시간 정보를 QDateTime 객체로 axisX.setRange( dt, dt.addSecs(self.viewLimit) ) #X축에 출력될 값의 범위를 현재 시간부터 viewLimit (120)초 이후까지 설정, 지정된 초 이후의 시간을 QDateTime으로 반환 axisY = QValueAxis() #정수를 저장하는 축을 생성 axisY.setVisible(False) #축의 레이블을 차트에 표시하지 않음 #X, Y축을 차트와 데이터에 연결 self.priceChart.addAxis(axisX, Qt.AlignBottom) self.priceChart.addAxis(axisY, Qt.AlignRight) self.priceData.attachAxis(axisX) self.priceData.attachAxis(axisY) self.priceChart.layout().setContentsMargins(0, 0, 0, 0) #여백을 최소화 def closeEvent(self, event): self.pw.close() def appendData(self, currPirce): if len(self.priceData) == self.viewLimit: #정해진 데이터 개수만큼 저장돼 있다면 self.priceData.remove(0) #오래된 0번 인덱스의 데이터를 삭제 dt = QDateTime.currentDateTime() #간과 현재가 (currPrice)를 함께 저장 self.priceData.append(dt.toMSecsSinceEpoch(), currPirce) self.__updateAxis() #차트의 축정보를 업데이트하는 __updateAxis() 메서드를 호출 def __updateAxis(self): #pointsVector 메서드를 사용해서 QLineSeries 객체에 저장된 데이터를 리스트로 얻어 옴 pvs = self.priceData.pointsVector() dtStart = QDateTime.fromMSecsSinceEpoch(int( pvs[0].x())) #가장 오래된 0번 인덱스 x 좌표에 저장된 값을 가져옴 if len(self.priceData) == self.viewLimit: dtLast = QDateTime.fromMSecsSinceEpoch(int(pvs[-1].x( ))) #마지막 데이터는 119 번 인덱스에 저장 = 최근 시간 정보가 들어 있는 마지막 객체를 선택 else: dtLast = dtStart.addSecs( self.viewLimit ) #viewLimit 보다 작다면 시작 위치 0번을 기준으로 viewLimit 초 이후까지 출력 ax = self.priceChart.axisX() ax.setRange(dtStart, dtLast) ay = self.priceChart.axisY() dataY = [v.y() for v in pvs] ay.setRange(min(dataY), max(dataY)) self.priceView.setChart(self.priceChart) self.priceView.setRenderHints( QPainter.Antialiasing) #차트에 anti-aliasing을 적용