示例#1
0
class PlotterPane(QChartView):
    """
    This plotter widget makes viewing sensor data easy!

    This widget represents a chart that will look for tuple data from
    the MicroPython REPL, Python 3 REPL or Python 3 code runner and will
    auto-generate a graph.
    """

    data_flood = pyqtSignal()

    def __init__(self, theme='day', parent=None):
        super().__init__(parent)
        # Holds the raw input to be checked for actionable data to display.
        self.input_buffer = []
        # Holds the raw actionable data detected while plotting.
        self.raw_data = []
        self.setObjectName('plotterpane')
        self.max_x = 100  # Maximum value along x axis
        self.max_y = 1000  # Maximum value +/- along y axis
        self.flooded = False  # Flag to indicate if data flooding is happening.

        # Holds deques for each slot of incoming data (assumes 1 to start with)
        self.data = [
            deque([0] * self.max_x),
        ]
        # Holds line series for each slot of incoming data (assumes 1 to start
        # with).
        self.series = [
            QLineSeries(),
        ]

        # Ranges used for the Y axis (up to 1000, after which we just double
        # the range).
        self.y_ranges = [1, 5, 10, 25, 50, 100, 250, 500, 1000]

        # Set up the chart with sensible defaults.
        self.chart = QChart()
        self.chart.legend().hide()
        self.chart.addSeries(self.series[0])
        self.axis_x = QValueAxis()
        self.axis_y = QValueAxis()
        self.axis_x.setRange(0, self.max_x)
        self.axis_y.setRange(-self.max_y, self.max_y)
        self.axis_x.setLabelFormat("time")
        self.axis_y.setLabelFormat("%d")
        self.chart.setAxisX(self.axis_x, self.series[0])
        self.chart.setAxisY(self.axis_y, self.series[0])
        self.setChart(self.chart)
        self.setRenderHint(QPainter.Antialiasing)

    def process_bytes(self, data):
        """
        Takes raw bytes and, if a valid tuple is detected, adds the data to
        the plotter.

        The the length of the bytes data > 1024 then a data_flood signal is
        emitted to ensure Mu can take action to remain responsive.
        """
        # Data flooding guards.
        if self.flooded:
            return
        if len(data) > 1024:
            self.flooded = True
            self.data_flood.emit()
            return
        data = data.replace(b'\r\n', b'\n')
        self.input_buffer.append(data)
        # Check if the data contains a Python tuple, containing numbers, on a
        # single line (i.e. ends with \n).
        input_bytes = b''.join(self.input_buffer)
        lines = input_bytes.split(b'\n')
        for line in lines:
            if line.startswith(b'(') and line.endswith(b')'):
                # Candidate tuple. Extract the raw bytes into a numeric tuple.
                raw_values = [val.strip() for val in line[1:-1].split(b',')]
                numeric_values = []
                for raw in raw_values:
                    try:
                        numeric_values.append(int(raw))
                        # It worked, so move onto the next value.
                        continue
                    except ValueError:
                        # Try again as a float.
                        pass
                    try:
                        numeric_values.append(float(raw))
                    except ValueError:
                        # Not an int or float, so ignore this value.
                        continue
                if numeric_values:
                    # There were numeric values in the tuple, so use them!
                    self.add_data(tuple(numeric_values))
        # Reset the input buffer.
        self.input_buffer = []
        if lines[-1]:
            # Append any bytes that are not yet at the end of a line, for
            # processing next time we read data from self.serial.
            self.input_buffer.append(lines[-1])

    def add_data(self, values):
        """
        Given a tuple of values, ensures there are the required number of line
        series, add the data to the line series, update the range of the chart
        so the chart displays nicely.
        """
        # Store incoming data to dump as CSV at the end of the session.
        self.raw_data.append(values)
        # Check the number of incoming values.
        if len(values) != len(self.series):
            # Adjust the number of line series.
            value_len = len(values)
            series_len = len(self.series)
            if value_len > series_len:
                # Add new line series.
                for i in range(value_len - series_len):
                    new_series = QLineSeries()
                    self.chart.addSeries(new_series)
                    self.chart.setAxisX(self.axis_x, new_series)
                    self.chart.setAxisY(self.axis_y, new_series)
                    self.series.append(new_series)
                    self.data.append(deque([0] * self.max_x))
            else:
                # Remove old line series.
                for old_series in self.series[value_len:]:
                    self.chart.removeSeries(old_series)
                self.series = self.series[:value_len]
                self.data = self.data[:value_len]

        # Add the incoming values to the data to be displayed, and compute
        # max range.
        max_ranges = []
        for i, value in enumerate(values):
            self.data[i].appendleft(value)
            max_ranges.append(max([max(self.data[i]), abs(min(self.data[i]))]))
            if len(self.data[i]) > self.max_x:
                self.data[i].pop()

        # Re-scale y-axis.
        max_y_range = max(max_ranges)
        y_range = bisect.bisect_left(self.y_ranges, max_y_range)
        if y_range < len(self.y_ranges):
            self.max_y = self.y_ranges[y_range]
        elif max_y_range > self.max_y:
            self.max_y += self.max_y
        elif max_y_range < self.max_y / 2:
            self.max_y = self.max_y / 2
        self.axis_y.setRange(-self.max_y, self.max_y)

        # Ensure floats are used to label y axis if the range is small.
        if self.max_y <= 5:
            self.axis_y.setLabelFormat("%2.2f")
        else:
            self.axis_y.setLabelFormat("%d")

        # Update the line series with the data.
        for i, line_series in enumerate(self.series):
            line_series.clear()
            xy_vals = []
            for j in range(self.max_x):
                val = self.data[i][self.max_x - 1 - j]
                xy_vals.append((j, val))
            for point in xy_vals:
                line_series.append(*point)

    def set_theme(self, theme):
        """
        Sets the theme / look for the plotter pane.
        """
        if theme == 'day':
            self.chart.setTheme(QChart.ChartThemeLight)
        elif theme == 'night':
            self.chart.setTheme(QChart.ChartThemeDark)
        else:
            self.chart.setTheme(QChart.ChartThemeHighContrast)
示例#2
0
class RTTView(QWidget):
    def __init__(self, parent=None):
        super(RTTView, self).__init__(parent)

        uic.loadUi('RTTView.ui', self)

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = ''

        self.daplink = None

        self.tmrRTT = QtCore.QTimer()
        self.tmrRTT.setInterval(10)
        self.tmrRTT.timeout.connect(self.on_tmrRTT_timeout)
        self.tmrRTT.start()

        self.tmrRTT_Cnt = 0

    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w', encoding='utf-8')

        self.conf = configparser.ConfigParser()
        self.conf.read('setting.ini', encoding='utf-8')

        if not self.conf.has_section('Memory'):
            self.conf.add_section('Memory')
            self.conf.set('Memory', 'StartAddr', '0x20000000')

    def initQwtPlot(self):
        self.PlotData = [[0] * 1000 for i in range(N_CURVES)]
        self.PlotPoint = [[QtCore.QPointF(j, 0) for j in range(1000)]
                          for i in range(N_CURVES)]

        self.PlotChart = QChart()

        self.ChartView = QChartView(self.PlotChart)
        self.ChartView.setVisible(False)
        self.vLayout.insertWidget(0, self.ChartView)

        self.PlotCurve = [QLineSeries() for i in range(N_CURVES)]

    @pyqtSlot()
    def on_btnOpen_clicked(self):
        if self.btnOpen.text() == u'打开连接':
            try:
                self.daplink = self.daplinks[self.cmbDAP.currentText()]
                self.daplink.open()

                _dp = dap.DebugPort(self.daplink, None)
                _dp.init()
                _dp.power_up_debug()

                _ap = ap.AHB_AP(_dp, 0)
                _ap.init()

                self.dap = cortex_m.CortexM(None, _ap)

                Addr = int(self.conf.get('Memory', 'StartAddr'), 16)
                for i in range(256):
                    buff = self.dap.read_memory_block8(Addr + 1024 * i, 1024)
                    buff = ''.join([chr(x) for x in buff])
                    index = buff.find('SEGGER RTT')
                    if index != -1:
                        self.RTTAddr = Addr + 1024 * i + index

                        buff = self.dap.read_memory_block8(
                            self.RTTAddr, ctypes.sizeof(SEGGER_RTT_CB))

                        rtt_cb = SEGGER_RTT_CB.from_buffer(bytearray(buff))
                        self.aUpAddr = self.RTTAddr + 16 + 4 + 4
                        self.aDownAddr = self.aUpAddr + ctypes.sizeof(
                            RingBuffer) * rtt_cb.MaxNumUpBuffers

                        self.txtMain.append(
                            '\n_SEGGER_RTT @ 0x%08X with %d aUp and %d aDown\n'
                            % (self.RTTAddr, rtt_cb.MaxNumUpBuffers,
                               rtt_cb.MaxNumDownBuffers))
                        break
                else:
                    raise Exception('Can not find _SEGGER_RTT')
            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')
            else:
                self.cmbDAP.setEnabled(False)
                self.btnOpen.setText(u'关闭连接')
        else:
            self.daplink.close()
            self.cmbDAP.setEnabled(True)
            self.btnOpen.setText(u'打开连接')

    def aUpRead(self):
        buf = self.dap.read_memory_block8(self.aUpAddr,
                                          ctypes.sizeof(RingBuffer))
        aUp = RingBuffer.from_buffer(bytearray(buf))

        if aUp.RdOff == aUp.WrOff:
            buf = []

        elif aUp.RdOff < aUp.WrOff:
            cnt = aUp.WrOff - aUp.RdOff
            buf = self.dap.read_memory_block8(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt)

            aUp.RdOff += cnt

            self.dap.write32(self.aUpAddr + 4 * 4, aUp.RdOff)

        else:
            cnt = aUp.SizeOfBuffer - aUp.RdOff
            buf = self.dap.read_memory_block8(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt)

            aUp.RdOff = 0  #这样下次再读就会进入执行上个条件

            self.dap.write32(self.aUpAddr + 4 * 4, aUp.RdOff)

        return ''.join([chr(x) for x in buf])

    def on_tmrRTT_timeout(self):
        if self.btnOpen.text() == u'关闭连接':
            try:
                self.rcvbuff += self.aUpRead()

                if self.txtMain.isVisible():
                    if self.chkHEXShow.isChecked():
                        text = ''.join(f'{ord(x):02X} ' for x in self.rcvbuff)
                    else:
                        text = self.rcvbuff

                    if len(self.txtMain.toPlainText()) > 25000:
                        self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

                    self.rcvbuff = ''

                else:
                    if self.rcvbuff.rfind(',') == -1: return

                    d = self.rcvbuff[0:self.rcvbuff.rfind(',')].split(
                        ',')  # [b'12', b'34'] or [b'12 34', b'56 78']
                    d = [[float(x) for x in X.strip().split()]
                         for X in d]  # [[12], [34]]   or [[12, 34], [56, 78]]
                    for arr in d:
                        for i, x in enumerate(arr):
                            if i == N_CURVES: break

                            self.PlotData[i].pop(0)
                            self.PlotData[i].append(x)
                            self.PlotPoint[i].pop(0)
                            self.PlotPoint[i].append(QtCore.QPointF(999, x))

                    self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(',') + 1:]

                    self.tmrRTT_Cnt += 1
                    if self.tmrRTT_Cnt % 4 == 0:
                        if len(d[-1]) != len(self.PlotChart.series()):
                            for series in self.PlotChart.series():
                                self.PlotChart.removeSeries(series)
                            for i in range(min(len(d[-1]), N_CURVES)):
                                self.PlotCurve[i].setName(f'Curve {i+1}')
                                self.PlotChart.addSeries(self.PlotCurve[i])
                            self.PlotChart.createDefaultAxes()

                        for i in range(len(self.PlotChart.series())):
                            for j, point in enumerate(self.PlotPoint[i]):
                                point.setX(j)

                            self.PlotCurve[i].replace(self.PlotPoint[i])

                        miny = min([
                            min(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        maxy = max([
                            max(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        self.PlotChart.axisY().setRange(miny, maxy)
                        self.PlotChart.axisX().setRange(0000, 1000)

            except Exception as e:
                self.rcvbuff = ''
                print(str(e))  # 波形显示模式下 txtMain 不可见,因此错误信息不能显示在其上

        else:
            self.tmrRTT_Cnt += 1
            if self.tmrRTT_Cnt % 20 == 0:
                self.detect_daplink()  # 自动检测 DAPLink 的热插拔

    def aDownWrite(self, text):
        buf = self.dap.read_memory_block8(self.aDownAddr,
                                          ctypes.sizeof(RingBuffer))
        aDown = RingBuffer.from_buffer(bytearray(buf))

        if aDown.WrOff >= aDown.RdOff:
            if aDown.RdOff != 0:
                cnt = min(aDown.SizeOfBuffer - aDown.WrOff, len(text))
            else:
                cnt = min(
                    aDown.SizeOfBuffer - 1 - aDown.WrOff,
                    len(text))  # 写入操作不能使得 aDown.WrOff == aDown.RdOff,以区分满和空
            self.dap.write_memory_block8(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, [ord(x) for x in text[:cnt]])

            aDown.WrOff += cnt
            if aDown.WrOff == aDown.SizeOfBuffer: aDown.WrOff = 0

            text = text[cnt:]

        if text and aDown.RdOff != 0 and aDown.RdOff != 1:  # != 0 确保 aDown.WrOff 折返回 0,!= 1 确保有空间可写入
            cnt = min(aDown.RdOff - 1 - aDown.WrOff,
                      len(text))  # - 1 确保写入操作不导致WrOff与RdOff指向同一位置
            self.dap.write_memory_block8(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, [ord(x) for x in text[:cnt]])

            aDown.WrOff += cnt

        self.dap.write32(self.aDownAddr + 4 * 3, aDown.WrOff)

    @pyqtSlot()
    def on_btnSend_clicked(self):
        if self.btnOpen.text() == u'关闭连接':
            text = self.txtSend.toPlainText()

            try:
                if self.chkHEXSend.isChecked():
                    text = ''.join([chr(int(x, 16)) for x in text.split()])

                self.aDownWrite(text)

            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')

    def detect_daplink(self):
        daplinks = aggregator.DebugProbeAggregator.get_all_connected_probes()

        if len(daplinks) != self.cmbDAP.count():
            self.cmbDAP.clear()
            for daplink in daplinks:
                self.cmbDAP.addItem(daplink.product_name)

            self.daplinks = collections.OrderedDict([
                (daplink.product_name, daplink) for daplink in daplinks
            ])

            if self.daplink and self.daplink.product_name in self.daplinks:
                self.cmbDAP.setCurrentIndex(self.daplinks.keys().index(
                    self.daplink.product_name))
            else:  # daplink被拔掉
                self.btnOpen.setText(u'打开连接')

    @pyqtSlot(int)
    def on_chkWavShow_stateChanged(self, state):
        self.ChartView.setVisible(state == Qt.Checked)
        self.txtMain.setVisible(state == Qt.Unchecked)

    @pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()

    def closeEvent(self, evt):
        self.conf.write(open('setting.ini', 'w', encoding='utf-8'))
示例#3
0
class StartWindow(QMainWindow):
    def __init__(self):
        super(StartWindow, self).__init__()

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.samplingRate = 0
        self.continueLoop = True
        self.chart = QChart()

        self.chart.setTitle('pressureGraph')

        self.chartView = QChartView(self.chart)
        self.ui.verticalLayout.addWidget(self.chartView)

        self.ui.samplingButton.clicked.connect(self.getSamplingRate)
        self.ui.resetButton.clicked.connect(self.startCollection)
        self.ui.stopButton.clicked.connect(self.stopLoop)

    def getSamplingRate(self):
        self.samplingRate = float(self.ui.frequencyEdit.text())
        print(self.samplingRate)

    def stopLoop(self):
        self.continueLoop = False

    def setAxis(self, time_passed, yList):
        horAxis = self.chart.axes(orientation=Qt.Horizontal)
        verAxis = self.chart.axes(orientation=Qt.Vertical)

        max_time = time_passed / self.samplingRate
        # set axis limits
        horAxis[0].setMax(max_time)
        horAxis[0].setMin(max(max_time - 10, 0))
        verAxis[0].setMax(max(yList))
        verAxis[0].setMin(min(yList))

    def process(self, series):
        time_passed = 0
        pList = list()
        while self.continueLoop:
            # to be able to get mouse input
            QApplication.processEvents()

            cur_pressure = dummy_pressure.get_pressure()
            print(cur_pressure)

            if len(series) != 0:
                self.chart.removeSeries(series)
            if len(series) > 10:
                # remove the first index after certain number of data
                series.remove(0)
                pList = pList[1:]
            series.append(time_passed / self.samplingRate, cur_pressure)
            pList.append(cur_pressure)
            self.chart.addSeries(series)

            # only create new axes in first instance to avoid clipping of labels.
            if len(series) < 2:
                self.chart.createDefaultAxes()

            self.ui.pressureDisplay.setText('{:.4f}'.format(cur_pressure))
            leak = False
            leakO = leak_check()
            leak = leakO.check_leak(cur_pressure)
            if not leak:
                self.ui.leakStatusBox.setText("No Pressure Drop detected")
            else:
                self.ui.leakStatusBox.setText(
                    "Pressure drop detected. Email sent")
                email_warning.send_message(pList)
                return

            self.setAxis(time_passed, pList)
            time.sleep(1 / self.samplingRate)
            #self.chart.removeAxis(horAxis[0])
            #self.chart.removeAxis(verAxis[0])
            time_passed += 1
        return

    def startCollection(self):
        series = QLineSeries()
        if self.samplingRate == 0:
            msgBox = QMessageBox()
            msgBox.setText('Set Sampling rate first')
            msgBox.exec()
            return
        self.continueLoop = True
        self.process(series)
        # t1 = threading.Thread(target=self.process, args = (series))
        print('stopped')
        return
示例#4
0
class RTTView(QWidget):
    def __init__(self, parent=None):
        super(RTTView, self).__init__(parent)

        uic.loadUi('RTTView.ui', self)

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = b''

        self.tmrRTT = QtCore.QTimer()
        self.tmrRTT.setInterval(10)
        self.tmrRTT.timeout.connect(self.on_tmrRTT_timeout)
        self.tmrRTT.start()

        self.tmrRTT_Cnt = 0

    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w', encoding='utf-8')

        self.conf = configparser.ConfigParser()
        self.conf.read('setting.ini', encoding='utf-8')

        if not self.conf.has_section('J-Link'):
            self.conf.add_section('J-Link')
            self.conf.set('J-Link', 'dllpath', '')
            self.conf.set('J-Link', 'mcucore', 'Cortex-M0')
            self.conf.add_section('Memory')
            self.conf.set('Memory', 'rttaddr', '0x20000000')

        self.linDLL.setText(self.conf.get('J-Link', 'dllpath'))
        self.linRTT.setText(self.conf.get('Memory', 'rttaddr'))
        self.cmbCore.setCurrentIndex(
            self.cmbCore.findText(self.conf.get('J-Link', 'mcucore')))

    def initQwtPlot(self):
        self.PlotData = [[0] * 1000 for i in range(N_CURVES)]
        self.PlotPoint = [[QtCore.QPointF(j, 0) for j in range(1000)]
                          for i in range(N_CURVES)]

        self.PlotChart = QChart()

        self.ChartView = QChartView(self.PlotChart)
        self.ChartView.setVisible(False)
        self.vLayout.insertWidget(0, self.ChartView)

        self.PlotCurve = [QLineSeries() for i in range(N_CURVES)]

    @pyqtSlot()
    def on_btnOpen_clicked(self):
        if self.btnOpen.text() == '打开连接':
            try:
                self.jlink = ctypes.cdll.LoadLibrary(self.linDLL.text())

                err_buf = (ctypes.c_char * 64)()
                self.jlink.JLINKARM_ExecCommand(
                    f'Device = {self.cmbCore.currentText()}'.encode('latin-1'),
                    err_buf, 64)

                self.jlink.JLINKARM_TIF_Select(1)
                self.jlink.JLINKARM_SetSpeed(4000)

                buff = ctypes.create_string_buffer(1024)
                Addr = int(self.linRTT.text(), 16)
                for i in range(128):
                    self.jlink.JLINKARM_ReadMem(Addr + 1024 * i, 1024, buff)
                    index = buff.raw.find(b'SEGGER RTT')
                    if index != -1:
                        self.RTTAddr = Addr + 1024 * i + index

                        buff = ctypes.create_string_buffer(
                            ctypes.sizeof(SEGGER_RTT_CB))
                        self.jlink.JLINKARM_ReadMem(
                            self.RTTAddr, ctypes.sizeof(SEGGER_RTT_CB), buff)

                        rtt_cb = SEGGER_RTT_CB.from_buffer(buff)
                        self.aUpAddr = self.RTTAddr + 16 + 4 + 4
                        self.aDownAddr = self.aUpAddr + ctypes.sizeof(
                            RingBuffer) * rtt_cb.MaxNumUpBuffers

                        self.txtMain.append(
                            f'\n_SEGGER_RTT @ 0x{self.RTTAddr:08X} with {rtt_cb.MaxNumUpBuffers} aUp and {rtt_cb.MaxNumDownBuffers} aDown\n'
                        )
                        break
                else:
                    raise Exception('Can not find _SEGGER_RTT')
            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')
            else:
                self.linDLL.setEnabled(False)
                self.btnDLL.setEnabled(False)
                self.linRTT.setEnabled(False)
                self.cmbCore.setEnabled(False)
                self.btnOpen.setText('关闭连接')
        else:
            self.linDLL.setEnabled(True)
            self.btnDLL.setEnabled(True)
            self.linRTT.setEnabled(True)
            self.cmbCore.setEnabled(True)
            self.btnOpen.setText('打开连接')

    def aUpRead(self):
        buf = ctypes.create_string_buffer(ctypes.sizeof(RingBuffer))
        self.jlink.JLINKARM_ReadMem(self.aUpAddr, ctypes.sizeof(RingBuffer),
                                    buf)

        aUp = RingBuffer.from_buffer(buf)

        if aUp.RdOff == aUp.WrOff:
            str = ctypes.create_string_buffer(0)

        elif aUp.RdOff < aUp.WrOff:
            cnt = aUp.WrOff - aUp.RdOff
            str = ctypes.create_string_buffer(cnt)
            self.jlink.JLINKARM_ReadMem(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt, str)

            aUp.RdOff += cnt

            self.jlink.JLINKARM_WriteU32(self.aUpAddr + 4 * 4, aUp.RdOff)

        else:
            cnt = aUp.SizeOfBuffer - aUp.RdOff
            str = ctypes.create_string_buffer(cnt)
            self.jlink.JLINKARM_ReadMem(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt, str)

            aUp.RdOff = 0  #这样下次再读就会进入执行上个条件

            self.jlink.JLINKARM_WriteU32(self.aUpAddr + 4 * 4, aUp.RdOff)

        return str.raw

    def on_tmrRTT_timeout(self):
        if self.btnOpen.text() == '关闭连接':
            try:
                self.rcvbuff += self.aUpRead()

                if self.txtMain.isVisible():
                    if self.chkHEXShow.isChecked():
                        text = ''.join(f'{x:02X} ' for x in self.rcvbuff)

                    else:
                        text = self.rcvbuff.decode('latin')

                    if len(self.txtMain.toPlainText()) > 25000:
                        self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

                    self.rcvbuff = b''

                else:
                    if self.rcvbuff.rfind(b',') == -1: return

                    d = self.rcvbuff[0:self.rcvbuff.rfind(b',')].split(
                        b',')  # [b'12', b'34'] or [b'12 34', b'56 78']
                    d = [[float(x) for x in X.strip().split()]
                         for X in d]  # [[12], [34]]   or [[12, 34], [56, 78]]
                    for arr in d:
                        for i, x in enumerate(arr):
                            if i == N_CURVES: break

                            self.PlotData[i].pop(0)
                            self.PlotData[i].append(x)
                            self.PlotPoint[i].pop(0)
                            self.PlotPoint[i].append(QtCore.QPointF(999, x))

                    self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(b',') + 1:]

                    self.tmrRTT_Cnt += 1
                    if self.tmrRTT_Cnt % 4 == 0:
                        if len(d[-1]) != len(self.PlotChart.series()):
                            for series in self.PlotChart.series():
                                self.PlotChart.removeSeries(series)
                            for i in range(min(len(d[-1]), N_CURVES)):
                                self.PlotCurve[i].setName(f'Curve {i+1}')
                                self.PlotChart.addSeries(self.PlotCurve[i])
                            self.PlotChart.createDefaultAxes()

                        for i in range(len(self.PlotChart.series())):
                            for j, point in enumerate(self.PlotPoint[i]):
                                point.setX(j)

                            self.PlotCurve[i].replace(self.PlotPoint[i])

                        miny = min([
                            min(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        maxy = max([
                            max(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        self.PlotChart.axisY().setRange(miny, maxy)
                        self.PlotChart.axisX().setRange(0000, 1000)

            except Exception as e:
                self.rcvbuff = b''
                print(str(e))  # 波形显示模式下 txtMain 不可见,因此错误信息不能显示在其上

    def aDownWrite(self, bytes):
        buf = ctypes.create_string_buffer(ctypes.sizeof(RingBuffer))
        self.jlink.JLINKARM_ReadMem(self.aDownAddr, ctypes.sizeof(RingBuffer),
                                    buf)

        aDown = RingBuffer.from_buffer(buf)

        if aDown.WrOff >= aDown.RdOff:
            if aDown.RdOff != 0:
                cnt = min(aDown.SizeOfBuffer - aDown.WrOff, len(bytes))
            else:
                cnt = min(
                    aDown.SizeOfBuffer - 1 - aDown.WrOff,
                    len(bytes))  # 写入操作不能使得 aDown.WrOff == aDown.RdOff,以区分满和空
            str = ctypes.create_string_buffer(bytes[:cnt])
            self.jlink.JLINKARM_WriteMem(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, cnt, str)

            aDown.WrOff += cnt
            if aDown.WrOff == aDown.SizeOfBuffer: aDown.WrOff = 0

            bytes = bytes[cnt:]

        if bytes and aDown.RdOff != 0 and aDown.RdOff != 1:  # != 0 确保 aDown.WrOff 折返回 0,!= 1 确保有空间可写入
            cnt = min(aDown.RdOff - 1 - aDown.WrOff,
                      len(bytes))  # - 1 确保写入操作不导致WrOff与RdOff指向同一位置
            str = ctypes.create_string_buffer(bytes[:cnt])
            self.jlink.JLINKARM_WriteMem(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, cnt, str)

            aDown.WrOff += cnt

        self.jlink.JLINKARM_WriteU32(self.aDownAddr + 4 * 3, aDown.WrOff)

    @pyqtSlot()
    def on_btnSend_clicked(self):
        if self.btnOpen.text() == '关闭连接':
            text = self.txtSend.toPlainText()

            try:
                if self.chkHEXSend.isChecked():
                    text = ''.join([chr(int(x, 16)) for x in text.split()])

                self.aDownWrite(text.encode('latin'))

            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')

    @pyqtSlot()
    def on_btnDLL_clicked(self):
        dllpath, filter = QFileDialog.getOpenFileName(
            caption='JLink_x64.dll路径',
            filter='动态链接库文件 (*.dll)',
            directory=self.linDLL.text())
        if dllpath != '':
            self.linDLL.setText(dllpath)

    @pyqtSlot(int)
    def on_chkWavShow_stateChanged(self, state):
        self.ChartView.setVisible(state == Qt.Checked)
        self.txtMain.setVisible(state == Qt.Unchecked)

    @pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()

    def closeEvent(self, evt):
        self.conf.set('J-Link', 'dllpath', self.linDLL.text())
        self.conf.set('J-Link', 'mcucore', self.cmbCore.currentText())
        self.conf.set('Memory', 'rttaddr', self.linRTT.text())
        self.conf.write(open('setting.ini', 'w', encoding='utf-8'))
示例#5
0
class LeftBarBarChart(QTabWidget):

    chart = None
    tabs = []
    bar_sets = []
    inputs = []
    main_tab = None

    def __init__(self, parent):
        super(LeftBarBarChart, self).__init__()
        self.setParent(parent)
        self.setStyleSheet("""
            color: white;
            border-radius: 4px;
            background:rgb(37,43,52,220);
        """)
        self.init_ui()

    def init_ui(self):
        # Tab Bar
        self.tab_bar = TabBarPlus(self)
        self.setTabBar(self.tab_bar)
        # Properties
        # self.setMovable(True)
        # Signals
        self.tab_bar.plusClicked.connect(self.add_tab)
        self.tab_bar.tabMoved.connect(self.tab_bar.movePlusButton)
        self.tabCloseRequested.connect(self.removeTab)
        #######
        self.chart = QChart()
        self.serie = QBarSeries()
        self.chart.addSeries(self.serie)
        self.chart.setTitle("Bar Chart")

        self.main_tab = MainTab(self)
        self.addTab(self.main_tab, self.main_tab.name)

        self.chart.createDefaultAxes()

        self.chart.setAnimationDuration(2000)
        self.chart.setAnimationOptions(QChart.AllAnimations)

    def add_tab(self):
        if self.count() >= 0:
            self.setTabsClosable(True)
        else:
            self.setTabsClosable(False)

        tab = Tab(self, "New Tab", len(self.main_tab.all_inputs))
        self.tabs.append(tab)
        self.addTab(tab, tab.name)

    def removeTab(self, p_int):
        if self.count() > 1:
            super(LeftBarBarChart, self).removeTab(p_int)
            del self.tabs[p_int - 1]

        if self.count() >= 2:
            self.setTabsClosable(True)
        else:
            self.setTabsClosable(False)

    def update_all_tabs(self, amount):
        for tab in self.tabs:
            tab.update_tab(amount)

    def update_axes(self):
        self.chart.removeSeries(self.serie)
        self.chart.addSeries(self.serie)
        self.chart.createDefaultAxes()
示例#6
0
class SERCOM(QWidget):
    def __init__(self, parent=None):
        super(SERCOM, self).__init__(parent)
        
        uic.loadUi('SERCOM.ui', self)

        for port, desc, hwid in comports():
            self.cmbPort.addItem(f'{port} ({desc[:desc.index("(")]})')

        self.ser = Serial()

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = b''

        self.tmrSer = QtCore.QTimer()
        self.tmrSer.setInterval(10)
        self.tmrSer.timeout.connect(self.on_tmrSer_timeout)
        self.tmrSer.start()

        self.tmrSer_Cnt = 0
        self.AutoInterval = 0   # 自动发送时间间隔,单位 10ms
    
    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w', encoding='utf-8')
        
        self.conf = configparser.ConfigParser()
        self.conf.read('setting.ini', encoding='utf-8')

        if not self.conf.has_section('serial'):
            self.conf.add_section('serial')
            self.conf.set('serial', 'port', 'COM0')
            self.conf.set('serial', 'baud', '57600')

            self.conf.add_section('encode')
            self.conf.set('encode', 'input', 'ASCII')
            self.conf.set('encode', 'output', 'ASCII')
            self.conf.set('encode', 'oenter', r'\r\n')  # output enter (line feed)

            self.conf.add_section('others')
            self.conf.set('others', 'history', '11 22 33 AA BB CC')

        self.txtSend.setPlainText(self.conf.get('others', 'history'))

        self.cmbICode.setCurrentIndex(self.cmbICode.findText(self.conf.get('encode', 'input')))
        self.cmbOCode.setCurrentIndex(self.cmbOCode.findText(self.conf.get('encode', 'output')))
        self.cmbEnter.setCurrentIndex(self.cmbEnter.findText(self.conf.get('encode', 'oenter')))

        index = self.cmbPort.findText(self.conf.get('serial', 'port'))
        self.cmbPort.setCurrentIndex(index if index != -1 else 0)
        self.cmbBaud.setCurrentIndex(self.cmbBaud.findText(self.conf.get('serial', 'baud')))
    
    def initQwtPlot(self):
        self.PlotData  = [[0]*1000 for i in range(N_CURVES)]
        self.PlotPoint = [[QtCore.QPointF(j, 0) for j in range(1000)] for i in range(N_CURVES)]

        self.PlotChart = QChart()

        self.ChartView = QChartView(self.PlotChart)
        self.ChartView.setVisible(False)
        self.vLayout0.insertWidget(0, self.ChartView)
        
        self.PlotCurve = [QLineSeries() for i in range(N_CURVES)]

    @pyqtSlot()
    def on_btnOpen_clicked(self):
        if not self.ser.is_open:
            try:
                self.ser.timeout = 1
                self.ser.xonxoff = 0
                self.ser.port = self.cmbPort.currentText().split()[0]
                self.ser.parity = self.cmbChek.currentText()[0]
                self.ser.baudrate = int(self.cmbBaud.currentText())
                self.ser.bytesize = int(self.cmbData.currentText())
                self.ser.stopbits = int(self.cmbStop.currentText())
                self.ser.open()
            except Exception as e:
                self.txtMain.clear()
                self.txtMain.insertPlainText(str(e))
            else:
                self.cmbPort.setEnabled(False)
                self.btnOpen.setText('关闭串口')
        else:
            self.ser.close()

            self.cmbPort.setEnabled(True)
            self.btnOpen.setText('打开串口')
    
    @pyqtSlot()
    def on_btnSend_clicked(self):
        if self.ser.is_open:
            text = self.txtSend.toPlainText()

            if self.cmbOCode.currentText() == 'HEX':
                try:
                    self.ser.write(bytes([int(x, 16) for x in text.split()]))
                except Exception as e:
                    print(e)

            else:
                if self.cmbEnter.currentText() == r'\r\n':
                    text = text.replace('\n', '\r\n')
                
                try:
                    self.ser.write(text.encode(self.cmbOCode.currentText()))
                except Exception as e:
                    print(e)
    
    def on_tmrSer_timeout(self):
        self.tmrSer_Cnt += 1

        if self.ser.is_open:
            if self.ser.in_waiting > 0:
                self.rcvbuff += self.ser.read(self.ser.in_waiting)
                
                if self.chkWave.isChecked():
                    if self.rcvbuff.rfind(b',') == -1:
                        return

                    try:
                        d = self.rcvbuff[0:self.rcvbuff.rfind(b',')].split(b',')    # [b'12', b'34'] or [b'12 34', b'56 78']
                        d = [[float(x) for x in X.strip().split()] for X in d]      # [[12], [34]]   or [[12, 34], [56, 78]]
                        for arr in d:
                            for i, x in enumerate(arr):
                                if i == N_CURVES: break

                                self.PlotData[i].pop(0)
                                self.PlotData[i].append(x)
                                self.PlotPoint[i].pop(0)
                                self.PlotPoint[i].append(QtCore.QPointF(999, x))
                        
                        self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(b',')+1:]

                        if self.tmrSer_Cnt % 4 == 0:
                            if len(d[-1]) != len(self.PlotChart.series()):
                                for series in self.PlotChart.series():
                                    self.PlotChart.removeSeries(series)
                                for i in range(min(len(d[-1]), N_CURVES)):
                                    self.PlotCurve[i].setName(f'Curve {i+1}')
                                    self.PlotChart.addSeries(self.PlotCurve[i])
                                self.PlotChart.createDefaultAxes()

                            for i in range(len(self.PlotChart.series())):
                                for j, point in enumerate(self.PlotPoint[i]):
                                    point.setX(j)
                            
                                self.PlotCurve[i].replace(self.PlotPoint[i])
                        
                            miny = min([min(d) for d in self.PlotData[:len(self.PlotChart.series())]])
                            maxy = max([max(d) for d in self.PlotData[:len(self.PlotChart.series())]])
                            self.PlotChart.axisY().setRange(miny, maxy)
                            self.PlotChart.axisX().setRange(0000, 1000)
                    
                    except Exception as e:
                        self.rcvbuff = b''
                        print(e)

                else:
                    text = ''
                    if self.cmbICode.currentText() == 'ASCII':
                        text = ''.join([chr(x) for x in self.rcvbuff])
                        self.rcvbuff = b''

                    elif self.cmbICode.currentText() == 'HEX':
                        text = ' '.join([f'{x:02X}' for x in self.rcvbuff]) + ' '
                        self.rcvbuff = b''

                    elif self.cmbICode.currentText() == 'GBK':
                        while len(self.rcvbuff):
                            if self.rcvbuff[0:1].decode('GBK', 'ignore'):
                                text += self.rcvbuff[0:1].decode('GBK')
                                self.rcvbuff = self.rcvbuff[1:]

                            elif len(self.rcvbuff) > 1 and self.rcvbuff[0:2].decode('GBK', 'ignore'):
                                text += self.rcvbuff[0:2].decode('GBK')
                                self.rcvbuff = self.rcvbuff[2:]

                            elif len(self.rcvbuff) > 1:
                                text += chr(self.rcvbuff[0])
                                self.rcvbuff = self.rcvbuff[1:]

                            else:
                                break

                    elif self.cmbICode.currentText() == 'UTF-8':
                        while len(self.rcvbuff):
                            if self.rcvbuff[0:1].decode('UTF-8', 'ignore'):
                                text += self.rcvbuff[0:1].decode('UTF-8')
                                self.rcvbuff = self.rcvbuff[1:]

                            elif len(self.rcvbuff) > 1 and self.rcvbuff[0:2].decode('UTF-8', 'ignore'):
                                text += self.rcvbuff[0:2].decode('UTF-8')
                                self.rcvbuff = self.rcvbuff[2:]

                            elif len(self.rcvbuff) > 2 and self.rcvbuff[0:3].decode('UTF-8', 'ignore'):
                                text += self.rcvbuff[0:3].decode('UTF-8')
                                self.rcvbuff = self.rcvbuff[3:]

                            elif len(self.rcvbuff) > 3 and self.rcvbuff[0:4].decode('UTF-8', 'ignore'):
                                text += self.rcvbuff[0:4].decode('UTF-8')
                                self.rcvbuff = self.rcvbuff[4:]

                            elif len(self.rcvbuff) > 3:
                                text += chr(self.rcvbuff[0])
                                self.rcvbuff = self.rcvbuff[1:]

                            else:
                                break
                    
                    if len(self.txtMain.toPlainText()) > 25000: self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

            if self.AutoInterval and self.tmrSer_Cnt % self.AutoInterval == 0:
                self.on_btnSend_clicked()

        else:
            if self.tmrSer_Cnt % 100 == 0:
                if len(comports()) != self.cmbPort.count():
                    self.cmbPort.clear()
                    for port, desc, hwid in comports():
                        self.cmbPort.addItem(f'{port} ({desc[:desc.index("(")]})')

    @pyqtSlot(str)
    def on_cmbAuto_currentIndexChanged(self, text):
        if self.cmbAuto.currentText() == 'NO Auto':
            self.AutoInterval = 0

        elif self.cmbAuto.currentText().endswith('s'):
            self.AutoInterval = float(self.cmbAuto.currentText()[:-1]) * 100

        elif self.cmbAuto.currentText().endswith('m'):
            self.AutoInterval = float(self.cmbAuto.currentText()[:-1]) * 100 * 60
    
    @pyqtSlot(int)
    def on_chkWave_stateChanged(self, state):
        self.ChartView.setVisible(state == Qt.Checked)
        self.txtMain.setVisible(state == Qt.Unchecked)
    
    @pyqtSlot(str)
    def on_cmbBaud_currentIndexChanged(self, text):
        self.ser.baudrate = int(text)
    
    @pyqtSlot(str)
    def on_cmbData_currentIndexChanged(self, text):
        self.ser.bytesize = int(text)
    
    @pyqtSlot(str)
    def on_cmbChek_currentIndexChanged(self, text):
        self.ser.parity = text[0]
    
    @pyqtSlot(str)
    def on_cmbStop_currentIndexChanged(self, text):
        self.ser.stopbits = int(text)
    
    @pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()
    
    def closeEvent(self, evt):
        self.ser.close()

        self.conf.set('serial', 'port', self.cmbPort.currentText())
        self.conf.set('serial', 'baud', self.cmbBaud.currentText())
        self.conf.set('encode', 'input', self.cmbICode.currentText())
        self.conf.set('encode', 'output', self.cmbOCode.currentText())
        self.conf.set('encode', 'oenter', self.cmbEnter.currentText())
        self.conf.set('others', 'history', self.txtSend.toPlainText())
        self.conf.write(open('setting.ini', 'w', encoding='utf-8'))
示例#7
0
class GraphicsScene(QGraphicsScene):
    def __init__(self):
        super(GraphicsScene, self).__init__()

        self.line_series = QLineSeries()
        self.chart = QChart()
        self.chart_view = QChartView(self.chart)
        self.chart_view.setMinimumSize(640, 480)

        self.model = ItemModel()
        self.model.signal_update_models.connect(self.update_axes)

        self.table_view = TableView()

        self.table_view.setModel(self.model)
        self.table_view.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.table_view.verticalHeader().setSectionResizeMode(
            QHeaderView.Stretch)

        self.chart.setAnimationOptions(QChart.AllAnimations)
        self.chart.setAnimationDuration(2000)

        self.line_series.setName("Line 1")

        self.mapper = QVXYModelMapper(self)
        self.mapper.setXColumn(0)
        self.mapper.setYColumn(1)
        self.mapper.setSeries(self.line_series)
        self.mapper.setModel(self.model)
        self.chart.addSeries(self.line_series)

        seriesColorHex = self.line_series.pen().color().name()
        self.model.add_mapping(seriesColorHex,
                               QRect(0, 0, 2, self.model.rowCount()))

        self.line_series2 = QLineSeries()
        self.line_series2.setName("Line 2")

        self.mapper2 = QVXYModelMapper(self)
        self.mapper2.setXColumn(2)
        self.mapper2.setYColumn(3)
        self.mapper2.setSeries(self.line_series2)
        self.mapper2.setModel(self.model)
        self.chart.addSeries(self.line_series2)

        seriesColorHex = self.line_series2.pen().color().name()
        self.model.add_mapping(seriesColorHex,
                               QRect(2, 0, 2, self.model.rowCount()))

        self.chart.createDefaultAxes()

        self.chart_view.setRenderHint(QPainter.Antialiasing)
        self.chart.resize(500, 400)
        self.chart.setFlag(QGraphicsItem.ItemIsMovable)
        self.chart.setFlag(QGraphicsItem.ItemIsSelectable)
        self.addItem(self.chart)

    @pyqtSlot()
    def update_axes(self):
        self.chart.removeSeries(self.line_series)
        self.chart.removeSeries(self.line_series2)
        self.chart.addSeries(self.line_series)
        self.chart.addSeries(self.line_series2)
        self.chart.createDefaultAxes()
示例#8
0
class MicroPythonGraphWidget(QWidget):
    """
    Class implementing the MicroPython graph widget.
    
    @signal dataFlood emitted to indicate, that too much data is received
    """
    dataFlood = pyqtSignal()

    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(MicroPythonGraphWidget, self).__init__(parent)

        self.__layout = QHBoxLayout()
        self.__layout.setContentsMargins(2, 2, 2, 2)
        self.setLayout(self.__layout)

        self.__chartView = QChartView(self)
        self.__chartView.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)
        self.__layout.addWidget(self.__chartView)

        self.__verticalLayout = QVBoxLayout()
        self.__verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.__layout.addLayout(self.__verticalLayout)

        self.__saveButton = QToolButton(self)
        self.__saveButton.setIcon(UI.PixmapCache.getIcon("fileSave"))
        self.__saveButton.setToolTip(self.tr("Press to save the raw data"))
        self.__saveButton.clicked.connect(self.on_saveButton_clicked)
        self.__verticalLayout.addWidget(self.__saveButton)
        self.__verticalLayout.setAlignment(self.__saveButton, Qt.AlignHCenter)

        spacerItem = QSpacerItem(20, 20, QSizePolicy.Minimum,
                                 QSizePolicy.Expanding)
        self.__verticalLayout.addItem(spacerItem)

        label = QLabel(self.tr("max. X:"))
        self.__verticalLayout.addWidget(label)
        self.__verticalLayout.setAlignment(label, Qt.AlignHCenter)

        self.__maxX = 100
        self.__maxXSpinBox = QSpinBox()
        self.__maxXSpinBox.setMinimum(100)
        self.__maxXSpinBox.setMaximum(1000)
        self.__maxXSpinBox.setSingleStep(100)
        self.__maxXSpinBox.setToolTip(
            self.tr("Enter the maximum number of data points to be plotted."))
        self.__maxXSpinBox.setValue(self.__maxX)
        self.__maxXSpinBox.setAlignment(Qt.AlignRight)
        self.__verticalLayout.addWidget(self.__maxXSpinBox)

        # holds the data to be checked for plotable data
        self.__inputBuffer = []
        # holds the raw data
        self.__rawData = []
        self.__dirty = False

        self.__maxY = 1000
        self.__flooded = False  # flag indicating a data flood

        self.__data = [deque([0] * self.__maxX)]
        self.__series = [QLineSeries()]

        # Y-axis ranges
        self.__yRanges = [1, 5, 10, 25, 50, 100, 250, 500, 1000]

        # setup the chart
        self.__chart = QChart()
        self.__chart.legend().hide()
        self.__chart.addSeries(self.__series[0])
        self.__axisX = QValueAxis()
        self.__axisX.setRange(0, self.__maxX)
        self.__axisX.setLabelFormat("time")
        self.__axisY = QValueAxis()
        self.__axisY.setRange(-self.__maxY, self.__maxY)
        self.__axisY.setLabelFormat("%d")
        self.__chart.setAxisX(self.__axisX, self.__series[0])
        self.__chart.setAxisY(self.__axisY, self.__series[0])
        self.__chartView.setChart(self.__chart)
        self.__chartView.setRenderHint(QPainter.Antialiasing)

        self.__maxXSpinBox.valueChanged.connect(self.__handleMaxXChanged)

    @pyqtSlot(bytes)
    def processData(self, data):
        """
        Public slot to process the raw data.
        
        It takes raw bytes, checks the data for a valid tuple of ints or
        floats and adds the data to the graph. If the the length of the bytes
        data is greater than 1024 then a dataFlood signal is emitted to ensure
        eric can take action to remain responsive.
        
        @param data raw data received from the connected device via the main
            device widget
        @type bytes
        """
        # flooding guard
        if self.__flooded:
            return

        if len(data) > 1024:
            self.__flooded = True
            self.dataFlood.emit()
            return

        # disable the inputs while processing data
        self.__saveButton.setEnabled(False)
        self.__maxXSpinBox.setEnabled(False)

        data = data.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
        self.__inputBuffer.append(data)

        # check if the data contains a Python tuple containing numbers (int
        # or float) on a single line
        inputBytes = b"".join(self.__inputBuffer)
        lines = inputBytes.splitlines(True)
        for line in lines:
            if not line.endswith(b"\n"):
                # incomplete line (last line); skip it
                break

            line = line.strip()
            if line.startswith(b"(") and line.endswith(b")"):
                # it may be a tuple we are interested in
                rawValues = [val.strip() for val in line[1:-1].split(b",")]
                values = []
                for raw in rawValues:
                    try:
                        values.append(int(raw))
                        # ok, it is an integer
                        continue
                    except ValueError:
                        # test for a float
                        pass
                    try:
                        values.append(float(raw))
                    except ValueError:
                        # it is not an int or float, ignore it
                        continue
                if values:
                    self.__addData(tuple(values))

        self.__inputBuffer = []
        if lines[-1] and not lines[-1].endswith(b"\n"):
            # Append any left over bytes for processing next time data is
            # received.
            self.__inputBuffer.append(lines[-1])

        # re-enable the inputs
        self.__saveButton.setEnabled(True)
        self.__maxXSpinBox.setEnabled(True)

    def __addData(self, values):
        """
        Private method to add a tuple of values to the graph.
        
        It ensures there are the required number of line series, adds the data
        to the line series and updates the range of the chart so the chart
        displays nicely.
        
        @param values tuple containing the data to be added
        @type tuple of int or float
        """
        # store incoming data to be able to dump it as CSV upon request
        self.__rawData.append(values)
        self.__dirty = True

        # check number of incoming values and adjust line series accordingly
        if len(values) != len(self.__series):
            valuesLen = len(values)
            seriesLen = len(self.__series)
            if valuesLen > seriesLen:
                # add a nwe line series
                for _index in range(valuesLen - seriesLen):
                    newSeries = QLineSeries()
                    self.__chart.addSeries(newSeries)
                    self.__chart.setAxisX(self.__axisX, newSeries)
                    self.__chart.setAxisY(self.__axisY, newSeries)
                    self.__series.append(newSeries)
                    self.__data.append(deque([0] * self.__maxX))
            else:
                # remove obsolete line series
                for oldSeries in self.__series[valuesLen:]:
                    self.__chart.removeSeries(oldSeries)
                self.__series = self.__series[:valuesLen]
                self.__data = self.__data[:valuesLen]

        # add the new values to the display and compute the maximum range
        maxRanges = []
        for index, value in enumerate(values):
            self.__data[index].appendleft(value)
            maxRanges.append(
                max([max(self.__data[index]),
                     abs(min(self.__data[index]))]))
            if len(self.__data[index]) > self.__maxX:
                self.__data[index].pop()

        # re-scale the y-axis
        maxYRange = max(maxRanges)
        yRange = bisect.bisect_left(self.__yRanges, maxYRange)
        if yRange < len(self.__yRanges):
            self.__maxY = self.__yRanges[yRange]
        elif maxYRange > self.__maxY:
            self.__maxY += self.__maxY
        elif maxYRange < self.__maxY / 2:
            self.__maxY /= 2
        self.__axisY.setRange(-self.__maxY, self.__maxY)

        # ensure that floats are used to label the y-axis if the range is small
        if self.__maxY <= 5:
            self.__axisY.setLabelFormat("%2.2f")
        else:
            self.__axisY.setLabelFormat("%d")

        # update the line series
        for index, series in enumerate(self.__series):
            series.clear()
            xyValues = []
            for x in range(self.__maxX):
                value = self.__data[index][self.__maxX - 1 - x]
                xyValues.append((x, value))
            for xy in xyValues:
                series.append(*xy)

    @pyqtSlot()
    def on_saveButton_clicked(self):
        """
        Private slot to save the raw data to a CSV file.
        """
        self.saveData()

    def hasData(self):
        """
        Public method to check, if the chart contains some valid data.
        
        @return flag indicating valid data
        @rtype bool
        """
        return len(self.__rawData) > 0

    def isDirty(self):
        """
        Public method to check, if the chart contains unsaved data.
        
        @return flag indicating unsaved data
        @rtype bool
        """
        return self.hasData() and self.__dirty

    def saveData(self):
        """
        Public method to save the dialog's raw data.
        
        @return flag indicating success
        @rtype bool
        """
        baseDir = (Preferences.getMultiProject("Workspace")
                   or os.path.expanduser("~"))
        dataDir = os.path.join(baseDir, "data_capture")

        if not os.path.exists(dataDir):
            os.makedirs(dataDir)

        # save the raw data as a CSV file
        fileName = "{0}.csv".format(time.strftime("%Y%m%d-%H%M%S"))
        fullPath = os.path.join(dataDir, fileName)
        try:
            csvFile = open(fullPath, "w")
            csvWriter = csv.writer(csvFile)
            csvWriter.writerows(self.__rawData)
            csvFile.close()

            self.__dirty = False
            return True
        except (IOError, OSError) as err:
            E5MessageBox.critical(
                self, self.tr("Save Chart Data"),
                self.tr("""<p>The chart data could not be saved into file"""
                        """ <b>{0}</b>.</p><p>Reason: {1}</p>""").format(
                            fullPath, str(err)))
            return False

    @pyqtSlot(int)
    def __handleMaxXChanged(self, value):
        """
        Private slot handling a change of the max. X spin box.
        
        @param value value of the spin box
        @type int
        """
        delta = value - self.__maxX
        if delta == 0:
            # nothing to change
            return
        elif delta > 0:
            # range must be increased
            for deq in self.__data:
                deq.extend([0] * delta)
        else:
            # range must be decreased
            data = []
            for deq in self.__data:
                data.append(deque(list(deq)[:value]))
            self.__data = data

        self.__maxX = value
        self.__axisX.setRange(0, self.__maxX)
示例#9
0
class RTTView(QWidget):
    def __init__(self, parent=None):
        super(RTTView, self).__init__(parent)

        uic.loadUi('RTTView.ui', self)

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = b''

        self.tmrRTT = QtCore.QTimer()
        self.tmrRTT.setInterval(10)
        self.tmrRTT.timeout.connect(self.on_tmrRTT_timeout)
        self.tmrRTT.start()

        self.tmrRTT_Cnt = 0
        self.tmrDAP_Cnt = 0

    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w', encoding='utf-8')

        self.conf = configparser.ConfigParser()
        self.conf.read('setting.ini', encoding='utf-8')

        if not self.conf.has_section('J-Link'):
            self.conf.add_section('J-Link')
            self.conf.set('J-Link', 'dllpath', '')
            self.conf.add_section('Memory')
            self.conf.set('Memory', 'rttaddr', '0x20000000')

        self.cmbDLL.addItem(self.conf.get('J-Link', 'dllpath'))
        self.linRTT.setText(self.conf.get('Memory', 'rttaddr'))

    def initQwtPlot(self):
        self.PlotData = [[0] * 1000 for i in range(N_CURVES)]
        self.PlotPoint = [[QtCore.QPointF(j, 0) for j in range(1000)]
                          for i in range(N_CURVES)]

        self.PlotChart = QChart()

        self.ChartView = QChartView(self.PlotChart)
        self.ChartView.setVisible(False)
        self.vLayout.insertWidget(0, self.ChartView)

        self.PlotCurve = [QLineSeries() for i in range(N_CURVES)]

    @pyqtSlot()
    def on_btnOpen_clicked(self):
        if self.btnOpen.text() == '打开连接':
            try:
                if self.cmbDLL.currentIndex() == 0:
                    self.xlk = xlink.XLink(
                        jlink.JLink(self.cmbDLL.currentText(), 'Cortex-M0'))

                else:
                    from pyocd.coresight import dap, ap, cortex_m
                    daplink = self.daplinks[self.cmbDLL.currentIndex() - 1]
                    daplink.open()

                    _dp = dap.DebugPort(daplink, None)
                    _dp.init()
                    _dp.power_up_debug()

                    _ap = ap.AHB_AP(_dp, 0)
                    _ap.init()

                    self.xlk = xlink.XLink(cortex_m.CortexM(None, _ap))

                addr = int(self.linRTT.text(), 16)
                for i in range(128):
                    data = self.xlk.read_mem_U8(addr + 1024 * i,
                                                1024 + 32)  # 多读32字节,防止搜索内容在边界处
                    index = bytes(data).find(b'SEGGER RTT')
                    if index != -1:
                        self.RTTAddr = addr + 1024 * i + index

                        data = self.xlk.read_mem_U8(
                            self.RTTAddr, ctypes.sizeof(SEGGER_RTT_CB))

                        rtt_cb = SEGGER_RTT_CB.from_buffer(bytearray(data))
                        self.aUpAddr = self.RTTAddr + 16 + 4 + 4
                        self.aDownAddr = self.aUpAddr + ctypes.sizeof(
                            RingBuffer) * rtt_cb.MaxNumUpBuffers

                        self.txtMain.append(
                            f'\n_SEGGER_RTT @ 0x{self.RTTAddr:08X} with {rtt_cb.MaxNumUpBuffers} aUp and {rtt_cb.MaxNumDownBuffers} aDown\n'
                        )
                        break

                else:
                    raise Exception('Can not find _SEGGER_RTT')

            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')

            else:
                self.cmbDLL.setEnabled(False)
                self.btnDLL.setEnabled(False)
                self.linRTT.setEnabled(False)
                self.btnOpen.setText('关闭连接')

        else:
            self.xlk.close()
            self.cmbDLL.setEnabled(True)
            self.btnDLL.setEnabled(True)
            self.linRTT.setEnabled(True)
            self.btnOpen.setText('打开连接')

    def aUpRead(self):
        data = self.xlk.read_mem_U8(self.aUpAddr, ctypes.sizeof(RingBuffer))

        aUp = RingBuffer.from_buffer(bytearray(data))

        if aUp.RdOff <= aUp.WrOff:
            cnt = aUp.WrOff - aUp.RdOff

        else:
            cnt = aUp.SizeOfBuffer - aUp.RdOff

        if 0 < cnt < 1024 * 1024:
            data = self.xlk.read_mem_U8(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt)

            aUp.RdOff = (aUp.RdOff + cnt) % aUp.SizeOfBuffer

            self.xlk.write_U32(self.aUpAddr + 4 * 4, aUp.RdOff)

        else:
            data = []

        return bytes(data)

    def aDownWrite(self, bytes):
        data = self.xlk.read_mem_U8(self.aDownAddr, ctypes.sizeof(RingBuffer))

        aDown = RingBuffer.from_buffer(bytearray(data))

        if aDown.WrOff >= aDown.RdOff:
            if aDown.RdOff != 0:
                cnt = min(aDown.SizeOfBuffer - aDown.WrOff, len(bytes))
            else:
                cnt = min(
                    aDown.SizeOfBuffer - 1 - aDown.WrOff,
                    len(bytes))  # 写入操作不能使得 aDown.WrOff == aDown.RdOff,以区分满和空
            self.xlk.write_mem(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, bytes[:cnt])

            aDown.WrOff += cnt
            if aDown.WrOff == aDown.SizeOfBuffer: aDown.WrOff = 0

            bytes = bytes[cnt:]

        if bytes and aDown.RdOff != 0 and aDown.RdOff != 1:  # != 0 确保 aDown.WrOff 折返回 0,!= 1 确保有空间可写入
            cnt = min(aDown.RdOff - 1 - aDown.WrOff,
                      len(bytes))  # - 1 确保写入操作不导致WrOff与RdOff指向同一位置
            self.xlk.write_mem(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, bytes[:cnt])

            aDown.WrOff += cnt

        self.xlk.write_U32(self.aDownAddr + 4 * 3, aDown.WrOff)

    def on_tmrRTT_timeout(self):
        if self.btnOpen.text() == '关闭连接':
            try:
                self.rcvbuff += self.aUpRead()

                if self.txtMain.isVisible():
                    if self.chkHEXShow.isChecked():
                        text = ''.join(f'{x:02X} ' for x in self.rcvbuff)

                    else:
                        text = self.rcvbuff.decode('latin')

                    if len(self.txtMain.toPlainText()) > 25000:
                        self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

                    self.rcvbuff = b''

                else:
                    if self.rcvbuff.rfind(b',') == -1: return

                    d = self.rcvbuff[0:self.rcvbuff.rfind(b',')].split(
                        b',')  # [b'12', b'34'] or [b'12 34', b'56 78']
                    d = [[float(x) for x in X.strip().split()]
                         for X in d]  # [[12], [34]]   or [[12, 34], [56, 78]]
                    for arr in d:
                        for i, x in enumerate(arr):
                            if i == N_CURVES: break

                            self.PlotData[i].pop(0)
                            self.PlotData[i].append(x)
                            self.PlotPoint[i].pop(0)
                            self.PlotPoint[i].append(QtCore.QPointF(999, x))

                    self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(b',') + 1:]

                    self.tmrRTT_Cnt += 1
                    if self.tmrRTT_Cnt % 4 == 0:
                        if len(d[-1]) != len(self.PlotChart.series()):
                            for series in self.PlotChart.series():
                                self.PlotChart.removeSeries(series)
                            for i in range(min(len(d[-1]), N_CURVES)):
                                self.PlotCurve[i].setName(f'Curve {i+1}')
                                self.PlotChart.addSeries(self.PlotCurve[i])
                            self.PlotChart.createDefaultAxes()

                        for i in range(len(self.PlotChart.series())):
                            for j, point in enumerate(self.PlotPoint[i]):
                                point.setX(j)

                            self.PlotCurve[i].replace(self.PlotPoint[i])

                        miny = min([
                            min(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        maxy = max([
                            max(d) for d in
                            self.PlotData[:len(self.PlotChart.series())]
                        ])
                        self.PlotChart.axisY().setRange(miny, maxy)
                        self.PlotChart.axisX().setRange(0000, 1000)

            except Exception as e:
                self.rcvbuff = b''
                print(str(e))  # 波形显示模式下 txtMain 不可见,因此错误信息不能显示在其上

        else:
            self.tmrDAP_Cnt += 1
            if self.tmrDAP_Cnt % 100 == 0:
                try:
                    from pyocd.probe import aggregator
                    self.daplinks = aggregator.DebugProbeAggregator.get_all_connected_probes(
                    )
                    if len(self.daplinks) != self.cmbDLL.count() - 1:
                        for i in range(1, self.cmbDLL.count()):
                            self.cmbDLL.removeItem(i)
                        for i, daplink in enumerate(self.daplinks):
                            self.cmbDLL.addItem(daplink.product_name)
                except Exception as e:
                    pass

    @pyqtSlot()
    def on_btnSend_clicked(self):
        if self.btnOpen.text() == '关闭连接':
            text = self.txtSend.toPlainText()

            try:
                if self.chkHEXSend.isChecked():
                    text = ''.join([chr(int(x, 16)) for x in text.split()])

                self.aDownWrite(text.encode('latin'))

            except Exception as e:
                self.txtMain.append(f'\n{str(e)}\n')

    @pyqtSlot()
    def on_btnDLL_clicked(self):
        dllpath, filter = QFileDialog.getOpenFileName(
            caption='JLink_x64.dll路径',
            filter='动态链接库文件 (*.dll)',
            directory=self.cmbDLL.itemText(0))
        if dllpath != '':
            self.cmbDLL.setItemText(0, dllpath)

    @pyqtSlot(int)
    def on_chkWavShow_stateChanged(self, state):
        self.ChartView.setVisible(state == Qt.Checked)
        self.txtMain.setVisible(state == Qt.Unchecked)

    @pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()

    def closeEvent(self, evt):
        self.conf.set('J-Link', 'dllpath', self.cmbDLL.itemText(0))
        self.conf.set('Memory', 'rttaddr', self.linRTT.text())
        self.conf.write(open('setting.ini', 'w', encoding='utf-8'))
示例#10
0
class LeftBarPieChart(QWidget):
    def __init__(self, parent):
        super(LeftBarPieChart, self).__init__()
        self.parent = parent
        self.setParent(parent)

        self.chart = None
        self.inputs = []
        self.layout = None
        self.layout_inp = None
        self.btn_add = None
        self.w_inp = None
        self.header = None

        self.init_ui()

    def init_ui(self):

        self.chart = QChart()
        self.chart.setTheme(QChart.ChartThemeBlueIcy)
        self.chart.setAnimationDuration(1000)
        self.chart.setAnimationOptions(QChart.AllAnimations)
        self.serie = QPieSeries()

        self.layout = QVBoxLayout()
        self.layout_inp = QVBoxLayout()
        self.w_inp = QWidget()
        self.w_inp.setStyleSheet("""
            .QWidget{
                background-color: transparent;
            }
        """)

        self.w_inp.setLayout(self.layout_inp)

        self.setLayout(self.layout)
        self.inputs.append(InputPieChart(self))

        self.btn_add = QPushButton("+")
        self.btn_remove = QPushButton("-")
        self.btn_add.clicked.connect(self.add_input)
        self.btn_remove.clicked.connect(self.remove_input)
        self.btn_add.setStyleSheet("""
            border: 0px;
            background: green;
            color: white;
        """)
        self.btn_remove.setStyleSheet("""
            border: 0px;
            background: red;
            color: white;
        """)
        self.header = HeaderPieChartInput()
        self.layout.addWidget(self.header)
        self.layout.addWidget(self.w_inp)
        self.layout.addStretch(-1)
        self.layout.addWidget(self.btn_remove)
        self.layout.addWidget(self.btn_add)
        self.layout_inp.addWidget(self.inputs[0])

        self.serie.append(self.inputs[0].name.text(), 2)

        # self.slice.setExploded()
        # self.slice.setLabelVisible()
        # self.slice.setPen(QPen(Qt.darkGreen,2))
        # self.slice.setBrush(Qt.green)

        self.chart.addSeries(self.serie)
        self.chart.setTitle("Pie Chart")

    def add_input(self):
        if len(self.inputs) < 10:
            self.inputs.append(InputPieChart(self))
            self.layout_inp.addWidget(self.inputs[-1])
            self.serie.append(self.inputs[-1].name.text(),
                              int(self.inputs[-1].value.text()))
            # for slice in self.serie.slices():
            #     slice.setBrush(Qt.green)

    def remove_input(self):
        if len(self.inputs) >= 1:
            self.layout_inp.removeWidget(self.inputs[-1])
            self.inputs[-1].hide()
            self.inputs.remove(self.inputs[-1])
            self.slc = self.serie.slices()[-1]
            self.serie.remove(self.slc)

            # for slice in self.serie.slices():
            #     slice.setBrush(Qt.green)

    def input_changed(self, txt):
        if self.serie in self.chart.series():
            self.chart.removeSeries(self.serie)
            self.serie = QPieSeries()
            for inp in self.inputs:
                self.serie.append(inp.name.text(), int(inp.value.text()))
            self.chart.addSeries(self.serie)
示例#11
0
class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.init_ui()

    def init_ui(self):



        self.line_series = QLineSeries()
        self.chart = QChart()
        self.chart_view = QChartView(self.chart)
        self.chart_view.setMinimumSize(640,480)

        self.model = ItemModel()
        self.model.signal_update_models.connect(self.update_axes)

        self.table_view = TableView()

        self.table_view.setModel(self.model)
        self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)

        self.chart.setAnimationOptions(QChart.AllAnimations)
        self.chart.setAnimationDuration(2000)

        self.line_series.setName("Line 1")

        self.mapper = QVXYModelMapper(self)
        self.mapper.setXColumn(0)
        self.mapper.setYColumn(1)
        self.mapper.setSeries(self.line_series)
        self.mapper.setModel(self.model)
        self.chart.addSeries(self.line_series)

        seriesColorHex = self.line_series.pen().color().name()
        self.model.add_mapping(seriesColorHex,
                               QRect(0,0,2, self.model.rowCount()))

        self.line_series2 = QLineSeries()
        self.line_series2.setName("Line 2")

        self.mapper2 = QVXYModelMapper(self)
        self.mapper2.setXColumn(2)
        self.mapper2.setYColumn(3)
        self.mapper2.setSeries(self.line_series2)
        self.mapper2.setModel(self.model)
        self.chart.addSeries(self.line_series2)


        seriesColorHex = self.line_series2.pen().color().name()
        self.model.add_mapping(seriesColorHex,
                               QRect(2, 0, 2, self.model.rowCount()))

        self.chart.createDefaultAxes()

        self.grid = QGridLayout()
        self.grid.addWidget(self.table_view,0,0)
        self.grid.addWidget(self.chart_view,0,1)

        self.chart_view.setRenderHint(QPainter.Antialiasing)
        self.cw = QWidget()
        self.cw.setLayout(self.grid)
        self.setCentralWidget(self.cw)
        self.resize(400,300)

    @pyqtSlot()
    def update_axes(self):
        self.chart.removeSeries(self.line_series)
        self.chart.removeSeries(self.line_series2)
        self.chart.addSeries(self.line_series)
        self.chart.addSeries(self.line_series2)
        self.chart.createDefaultAxes()
示例#12
0
文件: panes.py 项目: martinohanlon/mu
class PlotterPane(QChartView):
    """
    This plotter widget makes viewing sensor data easy!

    This widget represents a chart that will look for tuple data from
    the MicroPython REPL, Python 3 REPL or Python 3 code runner and will
    auto-generate a graph.
    """

    data_flood = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        # Holds the raw input to be checked for actionable data to display.
        self.input_buffer = []
        # Holds the raw actionable data detected while plotting.
        self.raw_data = []
        self.setObjectName('plotterpane')
        self.max_x = 100  # Maximum value along x axis
        self.max_y = 1000  # Maximum value +/- along y axis
        self.flooded = False  # Flag to indicate if data flooding is happening.

        # Holds deques for each slot of incoming data (assumes 1 to start with)
        self.data = [deque([0] * self.max_x), ]
        # Holds line series for each slot of incoming data (assumes 1 to start
        # with).
        self.series = [QLineSeries(), ]

        # Ranges used for the Y axis (up to 1000, after which we just double
        # the range).
        self.y_ranges = [1, 5, 10, 25, 50, 100, 250, 500, 1000]

        # Set up the chart with sensible defaults.
        self.chart = QChart()
        self.chart.legend().hide()
        self.chart.addSeries(self.series[0])
        self.axis_x = QValueAxis()
        self.axis_y = QValueAxis()
        self.axis_x.setRange(0, self.max_x)
        self.axis_y.setRange(-self.max_y, self.max_y)
        self.axis_x.setLabelFormat("time")
        self.axis_y.setLabelFormat("%d")
        self.chart.setAxisX(self.axis_x, self.series[0])
        self.chart.setAxisY(self.axis_y, self.series[0])
        self.setChart(self.chart)
        self.setRenderHint(QPainter.Antialiasing)

    def process_bytes(self, data):
        """
        Takes raw bytes and, if a valid tuple is detected, adds the data to
        the plotter.

        The the length of the bytes data > 1024 then a data_flood signal is
        emitted to ensure Mu can take action to remain responsive.
        """
        # Data flooding guards.
        if self.flooded:
            return
        if len(data) > 1024:
            self.flooded = True
            self.data_flood.emit()
            return
        data = data.replace(b'\r\n', b'\n')
        self.input_buffer.append(data)
        # Check if the data contains a Python tuple, containing numbers, on a
        # single line (i.e. ends with \n).
        input_bytes = b''.join(self.input_buffer)
        lines = input_bytes.split(b'\n')
        for line in lines:
            if line.startswith(b'(') and line.endswith(b')'):
                # Candidate tuple. Extract the raw bytes into a numeric tuple.
                raw_values = [val.strip() for val in line[1:-1].split(b',')]
                numeric_values = []
                for raw in raw_values:
                    try:
                        numeric_values.append(int(raw))
                        # It worked, so move onto the next value.
                        continue
                    except ValueError:
                        # Try again as a float.
                        pass
                    try:
                        numeric_values.append(float(raw))
                    except ValueError:
                        # Not an int or float, so ignore this value.
                        continue
                if numeric_values:
                    # There were numeric values in the tuple, so use them!
                    self.add_data(tuple(numeric_values))
        # Reset the input buffer.
        self.input_buffer = []
        if lines[-1]:
            # Append any bytes that are not yet at the end of a line, for
            # processing next time we read data from self.serial.
            self.input_buffer.append(lines[-1])

    def add_data(self, values):
        """
        Given a tuple of values, ensures there are the required number of line
        series, add the data to the line series, update the range of the chart
        so the chart displays nicely.
        """
        # Store incoming data to dump as CSV at the end of the session.
        self.raw_data.append(values)
        # Check the number of incoming values.
        if len(values) != len(self.series):
            # Adjust the number of line series.
            value_len = len(values)
            series_len = len(self.series)
            if value_len > series_len:
                # Add new line series.
                for i in range(value_len - series_len):
                    new_series = QLineSeries()
                    self.chart.addSeries(new_series)
                    self.chart.setAxisX(self.axis_x, new_series)
                    self.chart.setAxisY(self.axis_y, new_series)
                    self.series.append(new_series)
                    self.data.append(deque([0] * self.max_x))
            else:
                # Remove old line series.
                for old_series in self.series[value_len:]:
                    self.chart.removeSeries(old_series)
                self.series = self.series[:value_len]
                self.data = self.data[:value_len]

        # Add the incoming values to the data to be displayed, and compute
        # max range.
        max_ranges = []
        for i, value in enumerate(values):
            self.data[i].appendleft(value)
            max_ranges.append(max([max(self.data[i]), abs(min(self.data[i]))]))
            if len(self.data[i]) > self.max_x:
                self.data[i].pop()

        # Re-scale y-axis.
        max_y_range = max(max_ranges)
        y_range = bisect.bisect_left(self.y_ranges, max_y_range)
        if y_range < len(self.y_ranges):
            self.max_y = self.y_ranges[y_range]
        elif max_y_range > self.max_y:
            self.max_y += self.max_y
        elif max_y_range < self.max_y / 2:
            self.max_y = self.max_y / 2
        self.axis_y.setRange(-self.max_y, self.max_y)

        # Ensure floats are used to label y axis if the range is small.
        if self.max_y <= 5:
            self.axis_y.setLabelFormat("%2.2f")
        else:
            self.axis_y.setLabelFormat("%d")

        # Update the line series with the data.
        for i, line_series in enumerate(self.series):
            line_series.clear()
            xy_vals = []
            for j in range(self.max_x):
                val = self.data[i][self.max_x - 1 - j]
                xy_vals.append((j, val))
            for point in xy_vals:
                line_series.append(*point)

    def set_theme(self, theme):
        """
        Sets the theme / look for the plotter pane.
        """
        if theme == 'day':
            self.chart.setTheme(QChart.ChartThemeLight)
        elif theme == 'night':
            self.chart.setTheme(QChart.ChartThemeDark)
        else:
            self.chart.setTheme(QChart.ChartThemeHighContrast)
示例#13
0
class My_Main_window(Ui_MainWindow, QWidget
                     ):  # 这里继承 QWidget 是因为 QFileDialog 必须要有 QWidget 才能运行,没有会出错
    def setupBusi(self, MainWindow):

        # # 设置布局
        # self.verticalLayout.addWidget(self.label)
        # self.verticalLayout.addWidget(self.pushButton)

        # 设置连接事件
        self.pushButton.clicked.connect(self.plot_)
        self.pushButton_2.clicked.connect(self.pixSave)

        self.chart = QChart()
        self.view = QChartView(self.chart)
        self.series = QLineSeries(self.chart)  # 在QChart上实例化曲线
        self.verticalLayout.addWidget(self.view)
        self.view.show()

    def plot_(self):

        # 设置图片背景颜色,默认是白色背景;比如,还可以设置为:QChart.ChartThemeDark,QChart.ChartThemeLight,QChart.ChartThemeBlueCerulean
        # QChart.ChartThemeBrownSand,QChart.ChartThemeBlueNcs,QChart.ChartThemeHighContrast,QChart.ChartThemeBlueIcy等
        self.view.chart().setTheme(QChart.ChartThemeBlueCerulean)

        self.chart.setTitle("曲线图")  # 设置图题
        self.chart.setTitleFont(QFont('SansSerif', 20))  # 设置图题字体的类型和大小
        # self.view.chart().legend().hide()  # 隐藏图例
        self.chart.removeSeries(self.series)  # 清除QChart上的指定曲线
        self.series.append(0, 6)
        self.series.append(1, 7)
        self.series.append(2, 4)
        self.series.setName('单井生产数据')  # 设置每条数据曲线的名称
        self.view.chart().legend().setFont(QFont("Arial",
                                                 17))  # 设置数据曲线名称的字体类型和大小
        self.chart.addSeries(self.series)
        self.chart.createDefaultAxes()  # 创建默认轴
        self.chart.axisX().setTitleText('时间')  # 设置横坐标标题
        self.chart.axisX().setLabelsFont(QFont('SansSerif',
                                               13))  # 设置横坐标刻度的字体类型和大小
        self.chart.axisX().setTitleFont(QFont('SansSerif',
                                              15))  # 设置横坐标标题字体的类型和大小
        self.chart.axisY().setTitleText('产量')  # 设置横坐标标题
        self.chart.axisY().setLabelsFont(QFont('SansSerif',
                                               13))  # 设置横坐标刻度的字体类型和大小
        self.chart.axisY().setTitleFont(QFont('SansSerif',
                                              15))  # 设置横坐标标题字体的类型和大小

        # self.series.remove(0)  # 清除数据,但坐标框架等还在。
        # self.series.clear()  # 清除数据,但坐标框架等还在。

        self.view.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        # self.view.show()

    def pixSave(self):

        # 用 QFileDialog 获取保存文件的全部路径(包括文件名和后缀名)
        fileName2, ok2 = QFileDialog.getSaveFileName(self, "文件保存", "/",
                                                     "图片文件 (*.png);;(*.jpeg)")
        """
        下面用 Pyqt5 的截屏方法,截取指定 QChartView 上显示的东西;缺点,若显示的图像变形,
        则截图也会变形!
        """
        screen = QApplication.primaryScreen()
        pix = screen.grabWindow(self.view.winId())
        pix.save(fileName2)
示例#14
0
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('Температура')