class MagneticApp(MainWindow): app_title = "Magnetic Viewer - {0}" TIMEOUT = 100 def __init__(self, data=None, title=None, *args, **kwargs): super().__init__(*args, **kwargs) self.setupUi() self.errors_data = 0 # Start/Stop monitor sensor data self.monitor_running = False # Start/Stop compensate self.compensate = False # On/Off logging data self.logging_enable = False # Search available serial ports available_ports = sensor.scan_ports() if not available_ports: self.status.showMessage("No available ports") for btn in self.modeButtonGroup.buttons(): btn.setDisabled(True) else: self.portbox.addItems(available_ports) # Connecting model to consumers self.model = SensorDataModel() self.centralWidget().table_view.setModel(self.model) # Connecting signal/slot self.toolbar_buttons['collection'].clicked.connect(self.on_compensate) self.toolbar_buttons['log_on'].clicked.connect(self.turn_logging) self.toolbar_buttons['select_path'].clicked.connect( self.on_select_path) # ...button group self.modeButtonGroup.buttonClicked[QAbstractButton].connect( self.on_run) self.viewButtonGroup.buttonClicked[QAbstractButton].connect( self.on_switch_mode) # ...comboboxs self.spin.valueChanged[int].connect(self.on_set_chart_xinterval) # ...models self.model.rowsInserted.connect(self.on_model_changed) def on_model_changed(self): self.counter.setText("Rx: {}".format(self.model.rowCount())) def timerEvent(self, QTimerEvent): """ Handler timer event, every 100ms""" # <1> Get data from sensor try: data = [ round(item, 1) for item in sensor.SENSOR_QUEUE.get(timeout=0.5) ] except queue.Empty: self.status.showMessage("No sensor data") if self.errors_data <= 10000: self.errors_data += 1 self.errors.setText("Err: {}".format(self.errors_data)) else: self.errors.setText("Err: {}".format(">10000")) return pid, r, p, h, hy_raw, hx_raw, hz_raw, hy, hx, hz = data # <2> Append data to model self.model.append_data((r, p, h, hy_raw, hx_raw, hz_raw, hy, hx, hz)) # <4> Show to data view self.data_view.update(r, p, h, hy_raw, hx_raw, hz_raw, hy, hx, hz) # <3> Apply correction algorithms if self.options['dub z'].checkState(): hy_raw, hx_raw, hz_raw = to_horizont(hy_raw, hx_raw, hz_raw, r, p) if self.options['dub soft-iron'].checkState(): try: heading = self.maxdub.correct_heading(hx, hy) fmt_value = '{0:.1f}' self.data_view2['heading'].setText(fmt_value.format(heading)) except AttributeError: self.options['dub soft-iron'].setCheckState(False) self.status.showMessage("Error! Please, calibrate", 1000) # <5> Update plot if self.options['update charts'].checkState(): self.charts['inclinometer'].update_plot(r, p) #self.charts['heading'].update_plot(h) #self.charts['magnitometer'].update_plot(hy, hx, hz) #FIXME: При включения графика девиации увеличивается в разы количество пропущенных сигналов #self.charts['deviation'].update_plot(hy, hx) # <6> Logging data if self.logging_enable: time = QtCore.QDateTime.currentDateTime().toString( "yyyy-MM-dd hh:mm:ss.zzz") path = self.lineedit.text() str_data = ",".join((str(x) for x in (time, hex(pid), r, p, h, hy_raw, hx_raw, hz_raw, hy, hx, hz))) str_data += '\n' self.status.showMessage(time) with open(path, 'a') as f: f.write(str_data) # <7> Compensate mode if self.compensate: self.calibrate.update(data) self.progress.setValue(self.calibrate.status()) time = QtCore.QDateTime.currentDateTime().toString( "yyyy-MM-dd hh:mm:ss.zzz") path = self.lineedit.text() #str_data = ",".join((str(x) for x in (time, hex(pid), r, p, h, hy_raw, hx_raw, hz_raw, hy, hx, hz))) str_data = ",".join((str(x) for x in (hy, hx))) str_data += '\n' with open(path, 'a') as f: f.write(str_data) def on_run(self, btn): name = btn.objectName() if name == 'start': self.on_start() elif name == 'stop': self.on_stop() def on_start(self): if self.monitor_running: return # Disable widgets self.portbox.setDisabled(True) self.toolbar_buttons['select_path'].setDisabled(True) self.toolbar_buttons['rescan'].setDisabled(True) # ...disable action rescan self.menus['file'].children()[1].setDisabled(True) # Set selectable port to sensor port = self.portbox.currentText() self.serobj = serobj = serial.Serial(port, timeout=0.1) self.sensor = sensor.Sensor(serobj) # Run recieve data to single thread self.t = threading.Thread(target=self.sensor.run, daemon=False) self.t.start() # Run timer self.timer_recieve = self.startTimer(self.TIMEOUT, timerType=QtCore.Qt.PreciseTimer) self.status.showMessage("Running") self.monitor_running = True def on_stop(self): if not self.monitor_running: return self.status.showMessage("Stop") # Enable widgets self.portbox.setEnabled(True) self.toolbar_buttons['rescan'].setEnabled(True) # ...enable action rescan self.menus['file'].children()[1].setEnabled(True) # Kill timer if self.timer_recieve: self.killTimer(self.timer_recieve) self.timer_recieve = None # terminate thread self.sensor.terminate() self.t.join(timeout=0.1) # Close port self.serobj.close() del self.sensor self.monitor_running = False def on_clear(self): self.model.reset() self.centralWidget().chart_view.clear_area() def on_switch_mode(self, btn): btn_name = btn.objectName() if btn_name == 'chart': self.stack.setCurrentIndex(0) elif btn_name == 'table': self.stack.setCurrentIndex(1) elif btn_name == 'compensate': self.stack.setCurrentIndex(2) import subprocess subprocess.run(["python", "magnetic_viewer.py"]) else: self.stack.setCurrentIndex(0) def on_compensate(self): if not self.monitor_running: self.status.showMessage("Please, connect sensor") return if not self.compensate: self.toolbar_buttons['collection'].setText('Stop') initial = float(self.data_view['heading'].text()) self.calibrate = Calibrate(initial) self.compensate = True self.status.showMessage('Start compensate', 1000) else: self.toolbar_buttons['collection'].setText('Collection') self.progress.setValue(0) self.maxdub = self.calibrate.compute() del self.calibrate self.compensate = False self.status.showMessage('Stop compensate', 1000) def turn_logging(self): """ On/Off logging. If logging ON then bottombar visible """ self.logging_enable = ~self.logging_enable if self.logging_enable: self.record_bar.setVisible(True) else: self.record_bar.setVisible(False) def on_set_chart_xinterval(self, interval): for chart in self.charts.values(): chart.set_xmax(interval) def on_select_path(self): ''' Select path to save log ''' if not os.path.exists(REPORT_PATH): try: os.mkdir(REPORT_PATH) except OSError: self.status.showMessage("Error creating path", 2000) fname, _ = QFileDialog.getSaveFileName(self, "Select Path", REPORT_PATH, "Log (*.log)") if fname: self.lineedit.setText(fname) def closeEvent(self, QCloseEvent): self._action_quit() def _action_quit(self): try: self.sensor.terminate() self.t.join(timeout=0.1) except AttributeError: pass QtCore.QCoreApplication.exit(0)