Exemple #1
0
    def setupChart(self):
        """Set up the GUI's series and chart."""
        # Collect x and y data values from the CSV file
        x_values, y_values = self.loadCSVFile()

        # Get the largest x and y values; Used for setting the chart's axes
        x_max, y_max = max(x_values), max(y_values)

        # Create numpy arrays from the x and y values
        x_values = np.array(x_values)
        y_values = np.array(y_values)

        # Calculate the regression line
        coefficients = linearRegression(x_values, y_values)

        # Create chart object
        chart = QChart()
        chart.setTitle("Auto Insurance for Geographical Zones in Sweden")
        chart.legend().hide()

        # Create scatter series and add points to the series
        scatter_series = QScatterSeries()
        scatter_series.setName("DataPoints")
        scatter_series.setMarkerSize(9.0)
        scatter_series.hovered.connect(self.displayPointInfo)

        for value in range(0, self.row_count - 1):
            scatter_series.append(x_values[value], y_values[value])
            scatter_series.setBorderColor(QColor('#000000'))

        # Create line series and add points to the series
        line_series = QLineSeries()
        line_series.setName("RegressionLine")

        # Calculate the regression line
        for x in x_values:
            y_pred = coefficients[0] + coefficients[1] * x
            line_series.append(x, y_pred)

        # Add both series to the chart and create x and y axes
        chart.addSeries(scatter_series)
        chart.addSeries(line_series)
        chart.createDefaultAxes()

        axis_x = chart.axes(Qt.Horizontal)
        axis_x[0].setTitleText("Number of Claims")
        axis_x[0].setRange(0, x_max)
        axis_x[0].setLabelFormat("%i")

        axis_y = chart.axes(Qt.Vertical)
        axis_y[0].setTitleText(
            "Total Payment in Swedish Kronor (in thousands)")
        axis_y[0].setRange(0, y_max + 20)

        # Create QChartView object for displaying the chart
        chart_view = QChartView(chart)

        v_box = QVBoxLayout()
        v_box.addWidget(chart_view)
        self.setLayout(v_box)
Exemple #2
0
def main():
    import sys

    app = QApplication(sys.argv)

    acmeSeries = QBoxPlotSeries()
    acmeSeries.setName("Acme Ltd")

    boxWhiskSeries = QBoxPlotSeries()
    boxWhiskSeries.setName("BoxWhisk Inc")

    acmeData = QFile(":acme")
    if not acmeData.open(QIODevice.ReadOnly | QIODevice.Text):
        sys.exit(1)

    dataReader = BoxDataReader(acmeData)
    while not dataReader.atEnd():
        _set = dataReader.readBox()
        if _set is not None:
            acmeSeries.append(_set)

    boxwhiskData = QFile(":boxwhisk")
    if not boxwhiskData.open(QIODevice.ReadOnly | QIODevice.Text):
        sys.exit(1)

    dataReader.readFile(boxwhiskData)
    while not dataReader.atEnd():
        _set = dataReader.readBox()
        if _set is not None:
            boxWhiskSeries.append(_set)

    chart = QChart()
    chart.addSeries(acmeSeries)
    chart.addSeries(boxWhiskSeries)
    chart.setTitle("Acme Ltd and BoxWhisk Inc share deviation in 2012")
    chart.setAnimationOptions(QChart.SeriesAnimations)

    chart.createDefaultAxes()
    chart.axes(Qt.Vertical)[0].setMin(15.0)
    chart.axes(Qt.Vertical)[0].setMax(34.0)

    chart.legend().setVisible(True)
    chart.legend().setAlignment(Qt.AlignBottom)

    chartView = QChartView(chart)
    chartView.setRenderHint(QPainter.Antialiasing)

    window = QMainWindow()
    window.setCentralWidget(chartView)
    window.resize(800, 600)
    window.show()

    sys.exit(app.exec_())
Exemple #3
0
def main():
    import sys

    a = QApplication(sys.argv)

    acmeSeries = QCandlestickSeries()
    acmeSeries.setName("Acme Ltd")
    acmeSeries.setIncreasingColor(QColor(Qt.green))
    acmeSeries.setDecreasingColor(QColor(Qt.red))

    acmeData = QFile(":acme")
    if not acmeData.open(QIODevice.ReadOnly | QIODevice.Text):
        sys.exit(1)

    categories = []

    dataReader = CandlestickDataReader(acmeData)
    while not dataReader.atEnd():
        _set = dataReader.readCandlestickSet()
        if _set is not None:
            acmeSeries.append(_set)
            categories.append(
                QDateTime.fromMSecsSinceEpoch(int(
                    _set.timestamp())).toString("dd"))

    chart = QChart()
    chart.addSeries(acmeSeries)
    chart.setTitle("Acme Ltd Historical Data (July 2015)")
    chart.setAnimationOptions(QChart.SeriesAnimations)

    chart.createDefaultAxes()

    axisX = chart.axes(Qt.Horizontal)[0]
    axisX.setCategories(categories)

    axisY = chart.axes(Qt.Vertical)[0]
    axisY.setMax(axisY.max() * 1.01)
    axisY.setMin(axisY.min() * 0.99)

    chart.legend().setVisible(True)
    chart.legend().setAlignment(Qt.AlignBottom)

    chartView = QChartView(chart)
    chartView.setRenderHint(QPainter.Antialiasing)

    window = QMainWindow()
    window.setCentralWidget(chartView)
    window.resize(800, 600)
    window.show()

    sys.exit(a.exec_())
Exemple #4
0
def main():
    import sys

    app = QApplication(sys.argv)

    series0 = QLineSeries()
    series1 = QLineSeries()

    series0 << QPointF(1, 5) << QPointF(3, 7) << QPointF(7, 6) << QPointF(
        9, 7) << QPointF(12, 6) << QPointF(16, 7) << QPointF(18, 5)
    series1 << QPointF(1, 3) << QPointF(3, 4) << QPointF(7, 3) << QPointF(
        8, 2) << QPointF(12, 3) << QPointF(16, 4) << QPointF(18, 3)

    series = QAreaSeries(series0, series1)
    series.setName("Batman")
    pen = QPen(0x059605)
    pen.setWidth(3)
    series.setPen(pen)

    gradient = QLinearGradient(QPointF(0, 0), QPointF(0, 1))
    gradient.setColorAt(0.0, QColor(0x3CC63C))
    gradient.setColorAt(1.0, QColor(0x26F626))
    gradient.setCoordinateMode(QGradient.ObjectBoundingMode)
    series.setBrush(gradient)

    chart = QChart()
    chart.addSeries(series)
    chart.setTitle("Simple areachart example")
    chart.createDefaultAxes()
    chart.axes(Qt.Horizontal)[0].setRange(0, 20)
    chart.axes(Qt.Vertical)[0].setRange(0, 10)

    chartView = QChartView(chart)
    chartView.setRenderHint(QPainter.Antialiasing)

    window = QMainWindow()
    window.setCentralWidget(chartView)
    window.resize(400, 300)
    window.show()

    sys.exit(app.exec_())
Exemple #5
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
Exemple #6
0
class QtAgentGraph(QChartView):
    def __init__(self, spec):
        super().__init__(None)
        self.spec = spec
        self.chart = QChart()
        self.chart.setTitle(str(self.spec.variable))
        self.chart.legend().hide()

        self.setMinimumWidth(400)
        self.setMinimumHeight(230)

        self.setChart(self.chart)
        self.setRenderHint(QPainter.Antialiasing)
        self.chart.createDefaultAxes()
        self.autoscale_y_axis = True
        if self.spec.min_y and self.spec.max_y:
            self.autoscale_y_axis = False
            self.chart.axes()[1].setRange(self.spec.min_y, self.spec.max_y)

        self.axis_x = QValueAxis()
        self.axis_y = QValueAxis()
        self.chart.addAxis(self.axis_x, Qt.AlignBottom)
        self.chart.addAxis(self.axis_y, Qt.AlignLeft)

        self._updates_per_second = 60
        self._data = []
        self._min = 0
        self._max = 0

    def clear(self):
        for chart in self.chart.series():
            chart.clear()
        self._data = []

    def update_data(self):
        for a in self.spec.agents:
            if not hasattr(a, "_agent_series"):
                a._agent_series = QLineSeries()
                a._agent_series.setColor(
                    QColor(a.color[0], a.color[1], a.color[2]))
                a._agent_series_data = [getattr(a, self.spec.variable)]
                self.chart.addSeries(a._agent_series)
                a._agent_series.attachAxis(self.chart.axisX())
                a._agent_series.attachAxis(self.chart.axisY())
            else:
                a._agent_series_data.append(getattr(a, self.spec.variable))

    def redraw(self):
        for a in self.spec.agents:
            if hasattr(a, "_agent_series") and len(a._agent_series_data) > 0:
                datapoint = sum(a._agent_series_data) / len(
                    a._agent_series_data)
                a._agent_series.append(
                    QPointF(
                        a._agent_series.count() / self._updates_per_second,
                        datapoint,
                    ))
                self._min = min(self._min, datapoint)
                self._max = max(self._max, datapoint)
                a._agent_series.setColor(
                    QColor(a.color[0], a.color[1], a.color[2]))
            a._agent_series_data = []
        if len(self.spec.agents) > 0:
            first_agent = self.spec.agents[0]
            if hasattr(first_agent, "_agent_series"):
                first_series = first_agent._agent_series
                self.chart.axes()[0].setRange(0, (first_series.count() - 1) /
                                              self._updates_per_second)
                diff = self._max - self._min
                if self.autoscale_y_axis:
                    if diff > 0:
                        self.chart.axes()[1].setRange(self._min, self._max)
                    else:
                        self.chart.axes()[1].setRange(self._min - 0.5,
                                                      self._max + 0.5)
Exemple #7
0
class QtGraph(QChartView):
    def __init__(self, spec):
        super().__init__(None)
        self.spec = spec
        self.chart = QChart()
        # self.chart.setTitle(str(self.spec.variables))
        # self.chart.legend().hide()

        for i in range(len(self.spec.variables)):
            series = QLineSeries()
            series.setColor(
                QColor(
                    self.spec.colors[i][0],
                    self.spec.colors[i][1],
                    self.spec.colors[i][2],
                ))
            series.setName(self.spec.variables[i])
            self.chart.addSeries(series)

        self.setMinimumWidth(400)
        self.setMinimumHeight(230)

        self.setChart(self.chart)
        self.setRenderHint(QPainter.Antialiasing)
        self.chart.createDefaultAxes()
        self.autoscale_y_axis = True
        if self.spec.min_y and self.spec.max_y:
            self.autoscale_y_axis = False
            self.chart.axes()[1].setRange(self.spec.min_y, self.spec.max_y)

        self._updates_per_second = 60
        self._data = []
        self._min = 0
        self._max = 0

    def clear(self):
        for chart in self.chart.series():
            chart.clear()
        self._data = []

    def add_data(self, data):
        self._data.append(data)

    def redraw(self):
        if len(self._data) > 0:
            for i in range(len(self.spec.variables)):
                data = [datapoint[i] for datapoint in self._data]
                datapoint = sum(data) / len(data)
                self.chart.series()[i].append(
                    QPointF(
                        self.chart.series()[i].count() /
                        self._updates_per_second,
                        datapoint,
                    ))
                self._min = min(self._min, datapoint)
                self._max = max(self._max, datapoint)
        self.chart.axes()[0].setRange(0, (self.chart.series()[0].count() - 1) /
                                      self._updates_per_second)
        diff = self._max - self._min
        if self.autoscale_y_axis:
            if diff > 0:
                self.chart.axes()[1].setRange(self._min, self._max)
            else:
                self.chart.axes()[1].setRange(self._min - 0.5, self._max + 0.5)
        self._data = []
Exemple #8
0
class IMUChartView(QChartView, BaseGraph):
    def __init__(self, parent=None):
        super().__init__(parent=parent)

        # Render on OpenGL
        self.setViewport(QOpenGLWidget())

        self.xvalues = {}

        self.chart = QChart()

        self.setChart(self.chart)
        self.chart.legend().setVisible(True)
        self.chart.legend().setAlignment(Qt.AlignTop)
        self.ncurves = 0
        self.setRenderHint(QPainter.Antialiasing)

        # Cursor (with chart as parent)
        self.cursor = QGraphicsLineItem(self.chart)

        self.cursor.setZValue(100.0)
        # self.scene().addItem(self.cursor)

        # Selection features
        # self.setRubberBand(QChartView.HorizontalRubberBand)
        self.selectionBand = QRubberBand(QRubberBand.Rectangle, self)
        self.selecting = False
        self.initialClick = QPoint()

        # Track mouse
        self.setMouseTracking(True)

        self.labelValue = QLabel(self)
        self.labelValue.setStyleSheet(
            "background-color: rgba(255,255,255,75%); color: black;")
        self.labelValue.setAlignment(Qt.AlignCenter)
        self.labelValue.setMargin(5)
        self.labelValue.setVisible(False)

        self.build_style()

        self.selection_start_time = None
        self.selection_stop_time = None
        self.cursor_time = None

    def build_style(self):
        self.setStyleSheet("QLabel{color:blue;}")
        self.chart.setTheme(QChart.ChartThemeBlueCerulean)
        self.setBackgroundBrush(QBrush(Qt.darkGray))
        self.chart.setPlotAreaBackgroundBrush(QBrush(Qt.black))
        self.chart.setPlotAreaBackgroundVisible(True)

    def save_as_png(self, file_path):
        pixmap = self.grab()

        child = self.findChild(QOpenGLWidget)

        painter = QPainter(pixmap)
        if child is not None:
            d = child.mapToGlobal(QPoint()) - self.mapToGlobal(QPoint())
            painter.setCompositionMode(QPainter.CompositionMode_SourceAtop)
            painter.drawImage(d, child.grabFramebuffer())

        painter.end()
        pixmap.save(file_path, 'PNG')

    #  def closeEvent(self, event):
    #     self.aboutToClose.emit(self)

    @staticmethod
    def decimate(xdata, ydata):
        # assert(len(xdata) == len(ydata))

        # Decimate only if we have too much data
        decimate_factor = len(xdata) / 100000.0
        # decimate_factor = 1.0

        if decimate_factor > 1.0:
            decimate_factor = int(np.floor(decimate_factor))
            # print('decimate factor', decimate_factor)

            x = np.ndarray(int(len(xdata) / decimate_factor), dtype=np.float64)
            y = np.ndarray(int(len(ydata) / decimate_factor), dtype=np.float64)
            # for i in range(len(x)):
            for i, _ in enumerate(x):
                index = i * decimate_factor
                # assert(index < len(xdata))
                x[i] = xdata[index]
                y[i] = ydata[index]
                if x[i] < x[0]:
                    print('timestamp error', x[i], x[0])
            return x, y
        else:
            return xdata, ydata

    @pyqtSlot(float, float)
    def axis_range_changed(self, min_value, max_value):
        # print('axis_range_changed', min, max)
        for axis in self.chart.axes():
            axis.applyNiceNumbers()

    def update_axes(self):

        # Get and remove all axes
        for axis in self.chart.axes():
            self.chart.removeAxis(axis)

        # Create new axes
        # Create axis X
        axisx = QDateTimeAxis()
        axisx.setTickCount(5)
        axisx.setFormat("dd MMM yyyy hh:mm:ss")
        axisx.setTitleText("Date")
        self.chart.addAxis(axisx, Qt.AlignBottom)

        # Create axis Y
        axisY = QValueAxis()
        axisY.setTickCount(5)
        axisY.setLabelFormat("%.3f")
        axisY.setTitleText("Values")
        self.chart.addAxis(axisY, Qt.AlignLeft)
        # axisY.rangeChanged.connect(self.axis_range_changed)

        ymin = None
        ymax = None

        # Attach axes to series, find min-max
        for series in self.chart.series():
            series.attachAxis(axisx)
            series.attachAxis(axisY)
            vect = series.pointsVector()
            # for i in range(len(vect)):
            for i, _ in enumerate(vect):
                if ymin is None:
                    ymin = vect[i].y()
                    ymax = vect[i].y()
                else:
                    ymin = min(ymin, vect[i].y())
                    ymax = max(ymax, vect[i].y())

        # Update range
        # print('min max', ymin, ymax)
        if ymin is not None:
            axisY.setRange(ymin, ymax)

        # Make the X,Y axis more readable
        # axisx.applyNiceNumbers()
        # axisY.applyNiceNumbers()

    def add_data(self, xdata, ydata, color=None, legend_text=None):
        curve = QLineSeries()
        pen = curve.pen()
        if color is not None:
            pen.setColor(color)
        pen.setWidthF(1.5)

        curve.setPen(pen)
        # curve.setPointsVisible(True)

        # curve.setUseOpenGL(True)

        self.total_samples = max(self.total_samples, len(xdata))

        # Decimate
        xdecimated, ydecimated = self.decimate(xdata, ydata)

        # Data must be in ms since epoch
        # curve.append(self.series_to_polyline(xdecimated * 1000.0, ydecimated))
        # self.reftime = datetime.datetime.fromtimestamp(xdecimated[0])

        # if len(xdecimated) > 0:
        #    xdecimated = xdecimated - xdecimated[0]

        xdecimated *= 1000  # No decimal expected
        points = []
        for i, _ in enumerate(xdecimated):
            # TODO hack
            # curve.append(QPointF(xdecimated[i], ydecimated[i]))
            points.append(QPointF(xdecimated[i], ydecimated[i]))

        curve.replace(points)
        points.clear()

        if legend_text is not None:
            curve.setName(legend_text)

        # Needed for mouse events on series
        self.chart.setAcceptHoverEvents(True)
        self.xvalues[self.ncurves] = np.array(xdecimated)

        # connect signals / slots
        # curve.clicked.connect(self.lineseries_clicked)
        # curve.hovered.connect(self.lineseries_hovered)

        # Add series
        self.chart.addSeries(curve)
        self.ncurves += 1
        self.update_axes()

    def update_data(self, xdata, ydata, series_id):
        if series_id < len(self.chart.series()):
            # Find start time to replace data
            current_series = self.chart.series()[series_id]
            current_points = current_series.pointsVector()

            # Find start and end indexes
            start_index = -1
            try:
                start_index = current_points.index(
                    QPointF(xdata[0] * 1000, ydata[0]))  # Right on the value!
                # print("update_data: start_index found exact match.")
            except ValueError:
                # print("update_data: start_index no exact match - scanning deeper...")
                for i, value in enumerate(current_points):
                    # print(str(current_points[i].x()) + " == " + str(xdata[0]*1000))
                    if current_points[i].x() == xdata[0] * 1000 or (
                            i > 0 and current_points[i - 1].x() <
                            xdata[0] * 1000 < current_points[i].x()):
                        start_index = i
                        # print("update_data: start_index found approximative match.")
                        break

            end_index = -1
            try:
                end_index = current_points.index(
                    QPointF(xdata[len(xdata) - 1] * 1000,
                            ydata[len(ydata) - 1]))  # Right on!
                # print("update_data: start_index found exact match.")
            except ValueError:
                # print("update_data: start_index no exact match - scanning deeper...")
                for i, value in enumerate(current_points):
                    # print(str(current_points[i].x()) + " == " + str(xdata[0]*1000))
                    if current_points[i].x(
                    ) == xdata[len(xdata) - 1] * 1000 or (
                            i > 0 and
                            current_points[i - 1].x() < xdata[len(xdata) - 1] *
                            1000 < current_points[i].x()):
                        end_index = i
                        # print("update_data: start_index found approximative match.")
                        break

            if start_index < 0 or end_index < 0:
                return

            # Decimate, if needed
            xdata, ydata = self.decimate(xdata, ydata)

            # Check if we have the same number of points for that range. If not, remove and replace!
            target_points = current_points[start_index:end_index]
            if len(target_points) != len(xdata):
                points = []
                for i, value in enumerate(xdata):
                    # TODO improve
                    points.append(QPointF(value * 1000, ydata[i]))

                new_points = current_points[0:start_index] + points[0:len(points)-1] + \
                             current_points[end_index:len(current_points)-1]

                current_series.replace(new_points)
                new_points.clear()

                # self.xvalues[series_id] = np.array(xdata)

        return

    @classmethod
    def set_title(cls, title):
        # print('Setting title: ', title)
        # self.chart.setTitle(title)
        return

    @staticmethod
    def series_to_polyline(xdata, ydata):
        """Convert series data to QPolygon(F) polyline

        This code is derived from PythonQwt's function named
        `qwt.plot_curve.series_to_polyline`"""

        # print('series_to_polyline types:', type(xdata[0]), type(ydata[0]))
        size = len(xdata)
        polyline = QPolygonF(size)

        # for i in range(0, len(xdata)):
        #   polyline[i] = QPointF(xdata[i] - xdata[0], ydata[i])
        for i, data in enumerate(xdata):
            polyline[i] = QPointF(data - xdata[0], ydata[i])

        # pointer = polyline.data()
        # dtype, tinfo = np.float, np.finfo  # integers: = np.int, np.iinfo
        # pointer.setsize(2*polyline.size()*tinfo(dtype).dtype.itemsize)
        # memory = np.frombuffer(pointer, dtype)
        # memory[:(size-1)*2+1:2] = xdata
        # memory[1:(size-1)*2+2:2] = ydata
        return polyline

    def add_test_data(self):

        # 100Hz, one day accelerometer values
        npoints = 1000 * 60 * 24

        xdata = np.linspace(0., 10., npoints)
        self.add_data(xdata, np.sin(xdata), color=Qt.red, legend_text='Acc. X')
        # self.add_data(xdata, np.cos(xdata), color=Qt.green, legend_text='Acc. Y')
        # self.add_data(xdata, np.cos(2 * xdata), color=Qt.blue, legend_text='Acc. Z')
        self.set_title(
            "Simple example with %d curves of %d points (OpenGL Accelerated Series)"
            % (self.ncurves, npoints))

    def mouseMoveEvent(self, e: QMouseEvent):
        if self.selecting:

            clicked_x = max(
                self.chart.plotArea().x(),
                min(
                    self.mapToScene(e.pos()).x(),
                    self.chart.plotArea().x() + self.chart.plotArea().width()))

            clicked_y = max(
                self.chart.plotArea().y(),
                min(
                    self.mapToScene(e.pos()).y(),
                    self.chart.plotArea().y() +
                    self.chart.plotArea().height()))

            current_pos = QPoint(clicked_x, clicked_y)  # e.pos()

            self.selection_stop_time = self.chart.mapToValue(
                QPointF(clicked_x, 0)).x()

            self.setCursorPosition(clicked_x, True)

            if self.interaction_mode == GraphInteractionMode.SELECT:
                if current_pos.x() < self.initialClick.x():
                    start_x = current_pos.x()
                    width = self.initialClick.x() - start_x
                else:
                    start_x = self.initialClick.x()
                    width = current_pos.x() - self.initialClick.x()

                self.selectionBand.setGeometry(
                    QRect(start_x,
                          self.chart.plotArea().y(), width,
                          self.chart.plotArea().height()))
                if self.selection_rec:
                    self.selection_rec.setRect(
                        QRectF(start_x,
                               self.chart.plotArea().y(), width,
                               self.chart.plotArea().height()))

            if self.interaction_mode == GraphInteractionMode.MOVE:
                new_pos = current_pos - self.initialClick
                self.chart.scroll(-new_pos.x(), new_pos.y())
                self.initialClick = current_pos

    def mousePressEvent(self, e: QMouseEvent):
        # Handling rubberbands
        # super().mousePressEvent(e)
        # Verify if click is inside plot area
        # if self.chart.plotArea().contains(e.pos()):

        self.selecting = True
        clicked_x = max(
            self.chart.plotArea().x(),
            min(
                self.mapToScene(e.pos()).x(),
                self.chart.plotArea().x() + self.chart.plotArea().width()))

        clicked_y = max(
            self.chart.plotArea().y(),
            min(
                self.mapToScene(e.pos()).y(),
                self.chart.plotArea().y() + self.chart.plotArea().height()))

        self.initialClick = QPoint(clicked_x, clicked_y)  # e.pos()

        self.selection_start_time = self.chart.mapToValue(QPointF(
            clicked_x, 0)).x()

        self.setCursorPosition(clicked_x, True)

        if self.interaction_mode == GraphInteractionMode.SELECT:
            self.selectionBand.setGeometry(
                QRect(self.initialClick.x(),
                      self.chart.plotArea().y(), 1,
                      self.chart.plotArea().height()))
            self.selectionBand.show()

        if self.interaction_mode == GraphInteractionMode.MOVE:
            QGuiApplication.setOverrideCursor(Qt.ClosedHandCursor)
            self.labelValue.setVisible(False)

    def mouseReleaseEvent(self, e: QMouseEvent):

        # Assure if click is inside plot area
        # Handling rubberbands (with min / max x)
        clicked_x = max(
            self.chart.plotArea().x(),
            min(
                self.mapToScene(e.pos()).x(),
                self.chart.plotArea().x() + self.chart.plotArea().width()))

        if self.interaction_mode == GraphInteractionMode.SELECT:
            self.selectionBand.hide()
            if clicked_x != self.mapToScene(self.initialClick).x():
                mapped_x = self.mapToScene(self.initialClick).x()
                if self.initialClick.x() < clicked_x:
                    self.setSelectionArea(mapped_x, clicked_x, True)
                else:
                    self.setSelectionArea(clicked_x, mapped_x, True)

        if self.interaction_mode == GraphInteractionMode.MOVE:
            QGuiApplication.restoreOverrideCursor()

        self.selecting = False

    def clearSelectionArea(self, emit_signal=False):
        if self.selection_rec:
            self.scene().removeItem(self.selection_rec)
            self.selection_rec = None

        if emit_signal:
            self.clearedSelectionArea.emit()

    def setSelectionArea(self, start_pos, end_pos, emit_signal=False):
        selection_brush = QBrush(QColor(153, 204, 255, 128))
        selection_pen = QPen(Qt.transparent)

        if self.selection_rec:
            self.scene().removeItem(self.selection_rec)

        self.selection_rec = self.scene().addRect(
            start_pos,
            self.chart.plotArea().y(), end_pos - start_pos,
            self.chart.plotArea().height(), selection_pen, selection_brush)
        if emit_signal:
            self.selectedAreaChanged.emit(
                self.chart.mapToValue(QPointF(start_pos, 0)).x(),
                self.chart.mapToValue(QPointF(end_pos, 0)).x())

    def setSelectionAreaFromTime(self,
                                 start_time,
                                 end_time,
                                 emit_signal=False):
        # Convert times to x values
        if isinstance(start_time, datetime.datetime):
            start_time = start_time.timestamp() * 1000
        if isinstance(end_time, datetime.datetime):
            end_time = end_time.timestamp() * 1000

        start_pos = self.chart.mapToPosition(QPointF(start_time, 0)).x()
        end_pos = self.chart.mapToPosition(QPointF(end_time, 0)).x()

        self.setSelectionArea(start_pos, end_pos)

    def setCursorPosition(self, pos, emit_signal=False):

        self.cursor_time = self.chart.mapToValue(QPointF(pos, 0)).x()

        # print (pos)
        pen = self.cursor.pen()
        pen.setColor(Qt.cyan)
        pen.setWidthF(1.0)
        self.cursor.setPen(pen)
        # On Top
        self.cursor.setZValue(100.0)

        area = self.chart.plotArea()
        x = pos
        y1 = area.y()
        y2 = area.y() + area.height()

        # self.cursor.set
        self.cursor.setLine(x, y1, x, y2)
        self.cursor.show()

        xmap_initial = self.chart.mapToValue(QPointF(pos, 0)).x()
        display = ''
        # '<i>' + (datetime.datetime.fromtimestamp(xmap + self.reftime.timestamp())).strftime('%d-%m-%Y %H:%M:%S') +
        # '</i><br />'
        ypos = 10
        last_val = None
        for i in range(self.ncurves):
            # Find nearest point
            idx = (np.abs(self.xvalues[i] - xmap_initial)).argmin()
            ymap = self.chart.series()[i].at(idx).y()
            xmap = self.chart.series()[i].at(idx).x()
            if i == 0:
                display += "<i>" + (datetime.datetime.fromtimestamp(xmap_initial/1000)).strftime('%d-%m-%Y %H:%M:%S:%f') + \
                           "</i>"

            # Compute where to display label
            if last_val is None or ymap > last_val:
                last_val = ymap
                ypos = self.chart.mapToPosition(QPointF(xmap, ymap)).y()
            if display != '':
                display += '<br />'

            display += self.chart.series()[i].name(
            ) + ': <b>' + '%.3f' % ymap + '</b>'

        self.labelValue.setText(display)
        self.labelValue.setGeometry(pos, ypos, 100, 100)
        self.labelValue.adjustSize()
        self.labelValue.setVisible(True)

        if emit_signal:
            self.cursorMoved.emit(xmap_initial)

        self.update()

    def setCursorPositionFromTime(self, timestamp, emit_signal=False):
        # Find nearest point
        if isinstance(timestamp, datetime.datetime):
            timestamp = timestamp.timestamp()
        pos = self.get_pos_from_time(timestamp)
        self.setCursorPosition(pos, emit_signal)

    def get_pos_from_time(self, timestamp):
        px = timestamp
        idx1 = (np.abs(self.xvalues[0] - px)).argmin()
        x1 = self.chart.series()[0].at(idx1).x()
        pos1 = self.chart.mapToPosition(QPointF(x1, 0)).x()
        idx2 = idx1 + 1
        if idx2 < len(self.chart.series()[0]):
            x2 = self.chart.series()[0].at(idx2).x()
            if x2 != x1:
                pos2 = self.chart.mapToPosition(QPointF(x2, 0)).x()
                x2 /= 1000
                x1 /= 1000
                pos = (((px - x1) / (x2 - x1)) * (pos2 - pos1)) + pos1
            else:
                pos = pos1
        else:
            pos = pos1
        return pos

    def resizeEvent(self, e: QResizeEvent):
        super().resizeEvent(e)

        # oldSize = e.oldSize()
        # newSize = e.size()

        # Update cursor from time
        if self.cursor_time:
            self.setCursorPositionFromTime(self.cursor_time / 1000.0)

        # Update selection
        if self.selection_rec:
            self.setSelectionAreaFromTime(self.selection_start_time,
                                          self.selection_stop_time)

    def zoom_in(self):
        self.chart.zoomIn()
        self.update_axes()

    def zoom_out(self):
        self.chart.zoomOut()
        self.update_axes()

    def zoom_area(self):
        if self.selection_rec:
            zoom_rec = self.selection_rec.rect()
            zoom_rec.setY(0)
            zoom_rec.setHeight(self.chart.plotArea().height())
            self.chart.zoomIn(zoom_rec)
            self.clearSelectionArea(True)
            self.update_axes()

    def zoom_reset(self):
        self.chart.zoomReset()
        self.update_axes()

    def get_displayed_start_time(self):
        min_x = self.chart.mapToScene(self.chart.plotArea()).boundingRect().x()
        xmap = self.chart.mapToValue(QPointF(min_x, 0)).x()
        return datetime.datetime.fromtimestamp(xmap / 1000)

    def get_displayed_end_time(self):
        max_x = self.chart.mapToScene(self.chart.plotArea()).boundingRect().x()
        max_x += self.chart.mapToScene(
            self.chart.plotArea()).boundingRect().width()
        xmap = self.chart.mapToValue(QPointF(max_x, 0)).x()
        return datetime.datetime.fromtimestamp(xmap / 1000)

    @property
    def is_zoomed(self):
        return self.chart.isZoomed()
Exemple #9
0
class Statistics(QWidget):
    """A statistics widget that displays information about studied time, shows grown plants, etc..."""

    def __init__(self, history, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.history = history

        chart = self.generate_chart()

        self.SPACING = 10
        self.MIN_WIDTH = 400
        self.MIN_HEIGHT = 200

        chart.setMinimumWidth(self.MIN_WIDTH)
        chart.setMinimumHeight(self.MIN_HEIGHT)

        image_layout = QVBoxLayout()

        self.plant_study = None  # the study record the plant is a part of
        self.plant: Optional[Plant] = None  # the plant being displayed

        self.plant_date_label = QLabel(self)
        self.plant_date_label.setAlignment(Qt.AlignLeft)

        self.plant_duration_label = QLabel(self)
        self.plant_duration_label.setAlignment(Qt.AlignRight)

        label_layout = QHBoxLayout()
        label_layout.addWidget(self.plant_date_label)
        label_layout.addWidget(self.plant_duration_label)

        self.canvas = Canvas(self)
        self.canvas.setMinimumWidth(self.MIN_HEIGHT)
        self.canvas.setMinimumHeight(self.MIN_HEIGHT)

        stacked_layout = QVBoxLayout()
        stacked_layout.addLayout(label_layout)
        stacked_layout.addWidget(self.canvas)

        image_control = QHBoxLayout()

        text_color = self.palette().text().color()

        self.age_slider = QSlider(Qt.Horizontal, minimum=0, maximum=1000, value=1000,
                                  valueChanged=self.slider_value_changed)

        self.left_button = QPushButton(self, clicked=self.left,
                                       icon=qtawesome.icon('fa5s.angle-left', color=text_color))
        self.right_button = QPushButton(self, clicked=self.right,
                                        icon=qtawesome.icon('fa5s.angle-right', color=text_color))
        self.save_button = QPushButton(self, clicked=self.save,
                                       icon=qtawesome.icon('fa5s.download', color=text_color))

        image_control.addWidget(self.left_button)
        image_control.addWidget(self.right_button)
        image_control.addSpacing(self.SPACING)
        image_control.addWidget(self.age_slider)
        image_control.addSpacing(self.SPACING)
        image_control.addWidget(self.save_button)

        image_layout.addLayout(stacked_layout)
        image_layout.addLayout(image_control)
        image_layout.setContentsMargins(self.SPACING, self.SPACING, self.SPACING, self.SPACING)

        separator = QFrame()
        separator.setStyleSheet(f"background-color: {self.palette().text().color().name()}")
        separator.setFixedWidth(1)

        main_layout = QGridLayout()
        main_layout.setHorizontalSpacing(self.SPACING * 2)
        main_layout.setColumnStretch(0, 1)
        main_layout.setColumnStretch(2, 0)
        main_layout.addWidget(chart, 0, 0)
        main_layout.addWidget(separator, 0, 1)
        main_layout.addLayout(image_layout, 0, 2)

        self.setLayout(main_layout)

        self.move()  # go to the most recent plant

        self.refresh()

    def generate_chart(self):
        """Generate the bar graph for the widget."""
        self.tags = [QBarSet(tag) for tag in ["Study"]]

        series = QStackedBarSeries()

        for set in self.tags:
            series.append(set)

        self.chart = QChart()
        self.chart.addSeries(series)
        self.chart.setTitle("Total time studied (minutes per day)")

        days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
        axis = QBarCategoryAxis()
        axis.append(days)

        self.chart.createDefaultAxes()
        self.chart.setAxisX(axis, series)
        self.chart.legend().setAlignment(Qt.AlignBottom)
        self.chart.legend().setVisible(False)
        self.chart.setTheme(QChart.ChartThemeQt)
        self.chart.setBackgroundVisible(False)
        self.chart.setBackgroundRoundness(0)
        self.chart.setMargins(QMargins(0, 0, 0, 0))
        self.chart.setTitleBrush(QBrush(self.palette().text().color()))

        yAxis = self.chart.axes(Qt.Vertical)[0]
        yAxis.setGridLineVisible(False)
        yAxis.setLabelFormat("%d")
        yAxis.setLinePenColor(self.palette().text().color())
        yAxis.setLabelsColor(self.palette().text().color())

        xAxis = self.chart.axes(Qt.Horizontal)[0]
        xAxis.setGridLineVisible(False)
        xAxis.setLinePenColor(self.palette().text().color())
        xAxis.setLabelsColor(self.palette().text().color())

        chartView = QChartView(self.chart)
        chartView.setRenderHint(QPainter.Antialiasing)

        return chartView

    def slider_value_changed(self):
        """Called when the slider value has changed. Sets the age of the plant and updates it."""
        if self.plant is not None:
            # makes it a linear function from 0 to whatever the duration was, so the plant appears to grow normally
            self.plant.set_age(
                self.plant.inverse_age_coefficient_function(self.age_slider.value() / self.age_slider.maximum() *
                                                            self.plant.age_coefficient_function(
                                                                self.plant_study["duration"])))
            self.canvas.update()

    def refresh(self):
        """Refresh the labels."""
        # clear tag values
        for tag in self.tags:
            tag.remove(0, tag.count())

        study_minutes = [0] * 7
        for study in self.history.get_studies():
            # TODO: don't just crash
            study_minutes[study["date"].weekday()] += study["duration"]

        for minutes in study_minutes:
            self.tags[0] << minutes

        # manually set the range of the y axis, because it doesn't for some reason
        yAxis = self.chart.axes(Qt.Vertical)[0]
        yAxis.setRange(0, max(study_minutes))

    def left(self):
        """Move to the left (older) plant."""
        self.move(-1)

    def right(self):
        """Move to the right (newer) plant."""
        self.move(1)

    def move(self, delta: int = 0):
        """Move to the left/right plant by delta. If no plant is currently being displayed or delta is 0, pick the
        latest one."""
        studies = self.history.get_studies()

        # if there are no plants to display, don't do anything
        if len(studies) == 0:
            return

        # if no plant is being displayed or 0 is provided, pick the last one
        if self.plant is None or delta == 0:
            index = -1

        # if one is, find it and move by delta
        else:
            current_index = self.history.get_studies().index(self.plant_study)

            index = max(min(current_index + delta, len(studies) - 1), 0)

        # TODO: check for correct formatting, don't just crash if it's wrong
        self.plant = pickle.loads(studies[index]["plant"])
        self.plant_study = studies[index]

        # TODO: check for correct formatting, don't just crash if it's wrong
        self.plant_date_label.setText(self.plant_study["date"].strftime("%-d/%-m/%Y"))
        self.plant_duration_label.setText(f"{int(self.plant_study['duration'])} minutes")

        self.canvas.set_drawable(self.plant)
        self.slider_value_changed()  # it didn't, but the code should act as if it did (update plant)

    def save(self):
        """Save the current state of the plant to a file."""
        if self.plant is not None:
            name, _ = QFileDialog.getSaveFileName(self, 'Save File', "", "SVG files (*.svg)")

            if name == "":
                return

            if not name.endswith(".svg"):
                name += ".svg"

            self.plant.save(name, 1000, 1000)
Exemple #10
0
class IMUChartView(QChartView):

    aboutToClose = pyqtSignal(QObject)
    cursorMoved = pyqtSignal(datetime.datetime)

    def __init__(self, parent=None):
        super(QChartView, self).__init__(parent=parent)

        #self.setFixedHeight(400)
        #self.setMinimumHeight(500)
        """self.setMaximumHeight(700)
        self.setFixedHeight(700)
        self.setMinimumWidth(1500)
        self.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)"""

        self.reftime = datetime.datetime.now()
        self.cursor = QGraphicsLineItem()
        self.scene().addItem(self.cursor)
        self.decim_factor = 1

        # self.setScene(QGraphicsScene())
        self.chart = QChart()
        # self.scene().addItem(self.chart)
        self.setChart(self.chart)
        self.chart.legend().setVisible(True)
        self.chart.legend().setAlignment(Qt.AlignTop)
        self.ncurves = 0
        self.setRenderHint(QPainter.Antialiasing)
        self.setRubberBand(QChartView.HorizontalRubberBand)

        # X, Y label on bottom
        # self.xTextItem = QGraphicsSimpleTextItem(self.chart)
        # self.xTextItem.setText('X: ')
        # self.yTextItem = QGraphicsSimpleTextItem(self.chart)
        # self.yTextItem.setText('Y: ')
        # self.update_x_y_coords()

        # Track mouse
        self.setMouseTracking(True)

        # Top Widgets
        newWidget = QWidget(self)
        newLayout = QHBoxLayout()
        newLayout.setContentsMargins(0, 0, 0, 0)
        newWidget.setLayout(newLayout)
        #labelx = QLabel(self)
        #labelx.setText('X:')
        #self.labelXValue = QLabel(self)
        #labely = QLabel(self)
        #labely.setText('Y:')
        #self.labelYValue = QLabel(self)

        # Test buttons
        #newLayout.addWidget(QToolButton(self))
        #newLayout.addWidget(QToolButton(self))
        #newLayout.addWidget(QToolButton(self))

        # Spacer
        #newLayout.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding, QSizePolicy.Minimum))

        # Labels
        """newLayout.addWidget(labelx)
        newLayout.addWidget(self.labelXValue)
        self.labelXValue.setMinimumWidth(200)
        self.labelXValue.setMaximumWidth(200)
        newLayout.addWidget(labely)
        newLayout.addWidget(self.labelYValue)
        self.labelYValue.setMinimumWidth(200)
        self.labelYValue.setMaximumWidth(200)
        """
        """if parent is not None:
            parent.layout().setMenuBar(newWidget)
        """
        # self.layout()

        self.build_style()

    def build_style(self):
        self.setStyleSheet("QLabel{color:blue;}")

        self.setBackgroundBrush(QBrush(Qt.darkGray))
        self.chart.setPlotAreaBackgroundBrush(QBrush(Qt.black))
        self.chart.setPlotAreaBackgroundVisible(True)

    def save_as_png(self, file_path):
        pixmap = self.grab()

        child = self.findChild(QOpenGLWidget)

        painter = QPainter(pixmap)
        if child is not None:
            d = child.mapToGlobal(QPoint()) - self.mapToGlobal(QPoint())
            painter.setCompositionMode(QPainter.CompositionMode_SourceAtop)
            painter.drawImage(d, child.grabFramebuffer())

        painter.end()
        pixmap.save(file_path, 'PNG')

    def closeEvent(self, QCloseEvent):
        self.aboutToClose.emit(self)

    @pyqtSlot(QPointF)
    def lineseries_clicked(self, point):
        print('lineseries clicked', point)

    @pyqtSlot(QPointF)
    def lineseries_hovered(self, point):
        print('lineseries hovered', point)

    def update_x_y_coords(self):
        pass
        # self.xTextItem.setPos(self.chart.size().width() / 2 - 100, self.chart.size().height() - 40)
        # self.yTextItem.setPos(self.chart.size().width() / 2 + 100, self.chart.size().height() - 40)

    def decimate(self, xdata, ydata):
        assert (len(xdata) == len(ydata))

        # Decimate only if we have too much data
        decimate_factor = len(xdata) / 100000.0

        if decimate_factor > 1.0:
            decimate_factor = int(np.floor(decimate_factor))
            #print('decimate factor', decimate_factor)
            # x = decimate(xdata, decimate_factor)
            # y = decimate(ydata, decimate_factor)
            self.decim_factor = decimate_factor
            x = np.ndarray(int(len(xdata) / decimate_factor), dtype=np.float64)
            y = np.ndarray(int(len(ydata) / decimate_factor), dtype=np.float64)
            for i in range(len(x)):
                index = i * decimate_factor
                assert (index < len(xdata))
                x[i] = xdata[index]
                y[i] = ydata[index]
                if x[i] < x[0]:
                    print('timestamp error', x[i], x[0])

            #print('return size', len(x), len(y), 'timestamp', x[0])
            return x, y
        else:
            return xdata, ydata

    @pyqtSlot(float, float)
    def axis_range_changed(self, min, max):
        #print('axis_range_changed', min, max)
        for axis in self.chart.axes():
            axis.applyNiceNumbers()

    def update_axes(self):

        # Get and remove all axes
        for axis in self.chart.axes():
            self.chart.removeAxis(axis)

        # Create new axes
        # Create axis X
        # axisX = QDateTimeAxis()
        # axisX.setTickCount(5)
        # axisX.setFormat("dd MMM yyyy")
        # axisX.setTitleText("Date")
        # self.chart.addAxis(axisX, Qt.AlignBottom)
        # axisX.rangeChanged.connect(self.axis_range_changed)

        axisX = QValueAxis()
        axisX.setTickCount(10)
        axisX.setLabelFormat("%li")
        axisX.setTitleText("Seconds")
        self.chart.addAxis(axisX, Qt.AlignBottom)
        # axisX.rangeChanged.connect(self.axis_range_changed)

        # Create axis Y
        axisY = QValueAxis()
        axisY.setTickCount(5)
        axisY.setLabelFormat("%.3f")
        axisY.setTitleText("Values")
        self.chart.addAxis(axisY, Qt.AlignLeft)
        # axisY.rangeChanged.connect(self.axis_range_changed)

        ymin = None
        ymax = None

        # Attach axes to series, find min-max
        for series in self.chart.series():
            series.attachAxis(axisX)
            series.attachAxis(axisY)
            vect = series.pointsVector()
            for i in range(len(vect)):
                if ymin is None:
                    ymin = vect[i].y()
                    ymax = vect[i].y()
                else:
                    ymin = min(ymin, vect[i].y())
                    ymax = max(ymax, vect[i].y())

        # Update range
        # print('min max', ymin, ymax)
        if ymin is not None:
            axisY.setRange(ymin, ymax)

        # Make the X,Y axis more readable
        axisX.applyNiceNumbers()
        # axisY.applyNiceNumbers()

    def add_data(self, xdata, ydata, color=None, legend_text=None):
        curve = QLineSeries()
        pen = curve.pen()
        if color is not None:
            pen.setColor(color)
        pen.setWidthF(1.5)
        curve.setPen(pen)

        #curve.setUseOpenGL(True)

        # Decimate
        xdecimated, ydecimated = self.decimate(xdata, ydata)

        # Data must be in ms since epoch
        # curve.append(self.series_to_polyline(xdecimated * 1000.0, ydecimated))
        for i in range(len(xdecimated)):
            # TODO hack
            x = xdecimated[i] - xdecimated[0]
            curve.append(QPointF(x, ydecimated[i]))

        self.reftime = datetime.datetime.fromtimestamp(xdecimated[0])

        if legend_text is not None:
            curve.setName(legend_text)

        # Needed for mouse events on series
        self.chart.setAcceptHoverEvents(True)

        # connect signals / slots
        # curve.clicked.connect(self.lineseries_clicked)
        # curve.hovered.connect(self.lineseries_hovered)

        # Add series
        self.chart.addSeries(curve)
        self.ncurves += 1
        self.update_axes()

    def set_title(self, title):
        # print('Setting title: ', title)
        #self.chart.setTitle(title)
        pass

    def series_to_polyline(self, xdata, ydata):
        """Convert series data to QPolygon(F) polyline

        This code is derived from PythonQwt's function named
        `qwt.plot_curve.series_to_polyline`"""

        # print('series_to_polyline types:', type(xdata[0]), type(ydata[0]))
        size = len(xdata)
        polyline = QPolygonF(size)

        for i in range(0, len(xdata)):
            polyline[i] = QPointF(xdata[i] - xdata[0], ydata[i])

        # pointer = polyline.data()
        # dtype, tinfo = np.float, np.finfo  # integers: = np.int, np.iinfo
        # pointer.setsize(2*polyline.size()*tinfo(dtype).dtype.itemsize)
        # memory = np.frombuffer(pointer, dtype)
        # memory[:(size-1)*2+1:2] = xdata
        # memory[1:(size-1)*2+2:2] = ydata
        return polyline

    def add_test_data(self):

        # 100Hz, one day accelerometer values
        npoints = 1000 * 60 * 24

        xdata = np.linspace(0., 10., npoints)
        self.add_data(xdata, np.sin(xdata), color=Qt.red, legend_text='Acc. X')
        # self.add_data(xdata, np.cos(xdata), color=Qt.green, legend_text='Acc. Y')
        # self.add_data(xdata, np.cos(2 * xdata), color=Qt.blue, legend_text='Acc. Z')
        self.set_title("Simple example with %d curves of %d points " \
                          "(OpenGL Accelerated Series)" \
                          % (self.ncurves, npoints))

    def mouseMoveEvent(self, e: QMouseEvent):
        # Handling rubberbands
        super().mouseMoveEvent(e)

        # Go back to seconds (instead of ms)
        """xmap = self.chart.mapToValue(e.pos()).x()
        ymap = self.chart.mapToValue(e.pos()).y()

        self.labelXValue.setText(str(datetime.datetime.fromtimestamp(xmap + self.reftime.timestamp())))
        self.labelYValue.setText(str(ymap))"""

        # self.xTextItem.setText('X: ' + str(datetime.datetime.fromtimestamp(xmap + self.reftime.timestamp())))
        # self.yTextItem.setText('Y: ' + str(ymap))

    def mousePressEvent(self, e: QMouseEvent):
        # Handling rubberbands
        super().mousePressEvent(e)

        self.setCursorPosition(e.pos().x(), True)

        pass

    def setCursorPosition(self, pos, emit_signal=False):
        # print (pos)
        pen = self.cursor.pen()
        pen.setColor(Qt.cyan)
        pen.setWidthF(1.0)
        self.cursor.setPen(pen)
        # On Top
        self.cursor.setZValue(100.0)

        area = self.chart.plotArea()
        x = pos
        y1 = area.y()
        y2 = area.y() + area.height()

        # self.cursor.set
        self.cursor.setLine(x, y1, x, y2)
        self.cursor.show()

        xmap = self.chart.mapToValue(QPointF(pos, 0)).x()
        ymap = self.chart.mapToValue(QPointF(pos, 0)).y()

        #self.labelXValue.setText(str(datetime.datetime.fromtimestamp(xmap + self.reftime.timestamp())))
        #self.labelYValue.setText(str(ymap))

        if emit_signal:
            self.cursorMoved.emit(
                datetime.datetime.fromtimestamp(xmap +
                                                self.reftime.timestamp()))

        self.update()

    def setCursorPositionFromTime(self, timestamp, emit_signal=False):
        # Converts timestamp to x value
        pos = self.chart.mapToPosition(
            QPointF((timestamp - self.reftime).total_seconds(), 0)).x()
        self.setCursorPosition(pos, emit_signal)

    def mouseReleaseEvent(self, e: QMouseEvent):
        # Handling rubberbands
        super().mouseReleaseEvent(e)
        pass

    def resizeEvent(self, e: QResizeEvent):
        super().resizeEvent(e)

        # Update cursor height
        area = self.chart.plotArea()
        line = self.cursor.line()
        self.cursor.setLine(line.x1(), area.y(), line.x2(),
                            area.y() + area.height())

        # self.scene().setSceneRect(0, 0, e.size().width(), e.size().height())
        # Need to reposition X,Y labels
        self.update_x_y_coords()
Exemple #11
0
            acmeSeries.append(line)
            categories.append(
                QDateTime.fromMSecsSinceEpoch(line.timestamp()).toString("dd"))

    # [2]

    # [3] create the chart
    chart = QChart()
    chart.addSeries(acmeSeries)
    chart.setTitle("Acme Ltd Historical Data (July 2015)")
    chart.setAnimationOptions(QChart.SeriesAnimations)
    # [3]

    # [4] set the axes properties
    chart.createDefaultAxes()
    axisX = chart.axes(Qt.Horizontal)[0]
    axisX.setCategories(categories)

    axisY = chart.axes(Qt.Vertical)[0]
    axisY.setMax(axisY.max() * 1.01)
    axisY.setMin(axisY.min() * 0.99)
    # [4]

    # [5]
    chart.legend().setVisible(True)
    chart.legend().setAlignment(Qt.AlignBottom)
    # [5]

    # [6] prepare the chart view
    chartView = QChartView(chart)
    chartView.setRenderHint(QPainter.Antialiasing)
Exemple #12
0
class SproutUI(QtWidgets.QMainWindow):
    def __init__(self):
        super(SproutUI, self).__init__()
        uic.loadUi('Sprout.ui', self)
        self.setWindowTitle("Sprout")
        self.setWindowIcon(QIcon('./Images/SproutIcon.ico'))

        self.get_default_path()

        self.save_window_ui = SaveWindow(self)

        self.chartView = None

        self.myThread = None
        # columns: wedges, rows: rings (r1(w1,w2,...,w7,wAvg),r2(...),r3(...),rAvg(...))
        self.densities = []
        self.measurement_data = []

        self.error_message = ""

        self.loading_image = QPixmap("./Images/LoadingImage")

    def ui(self):
        """
        Sets Sprout user interface and displays the user interface
        :return: None
        """
        self.tabWidget_1.setCurrentIndex(0)
        self.tabWidget_2.setCurrentIndex(0)
        self.lineEdit_numMeasurements.setFocus(0)

        # Main Screen and save button
        self.browse_button_1.clicked.connect(self.browse_file)
        self.browse_button_2.clicked.connect(self.browse_folder)
        self.lineEdit_intermediateStepPath.setText(in_data['intermediate_path'])

        self.lineEdit_numMeasurements.editingFinished.connect(self.update_num_diameter_measurements)
        self.lineEdit_numWedges.editingFinished.connect(self.update_num_wedges)
        self.lineEdit_numRings.editingFinished.connect(self.update_num_rings)
        self.lineEdit_imageDPI.editingFinished.connect(self.update_image_dpi)

        self.start_button.clicked.connect(self.start_button_func)
        self.stop_button.clicked.connect(self.stop_button_func)
        self.save_button.clicked.connect(self.show_save_files)

        # Graphs View
        self.comboBox_rings.currentIndexChanged.connect(self.filter_rings_graph)
        self.comboBox_wedges.currentIndexChanged.connect(self.filter_wedges_graph)

        self.progressBar.setValue(0)
        self.progressBar.hide()
        self.label_progressBar.hide()

        self.disable_dashboard()

        self.progressBar.valueChanged.connect(self.progress_change)

        self.tabWidget_1.tabBar().setCursor(QtCore.Qt.PointingHandCursor)
        self.tabWidget_2.tabBar().setCursor(QtCore.Qt.PointingHandCursor)

        self.show()

    def get_default_path(self):
        """
        Gets the "Documents" directory of the user of the local computer.
        :return: None
        """
        temp = str(os.path.expanduser("~"))
        split = temp.split(os.path.sep)
        temp = str(split[0] + "/" + split[1] + "/" + split[2] + "/" + "Documents/Sprout/Run")
        in_data['intermediate_path'] = temp

    def browse_file(self):
        """
        Function mapped to the browse button used to select the file path of the image that will be used to
        calculate the fiber density of a bamboo cross-section.
        :return: None
        """
        url = QFileDialog.getOpenFileName(self, "Open a file", "", "*jpg; *jpeg;; *bmp;; *tif")

        if url[0] is not '':
            try:
                self.label_bamboo.setPixmap(self.loading_image)
                self.label_bamboo.repaint()
            except Exception:
                self.warning_message_box("Missing loading image. ")

            try:
                assert (url[0])[-4:] in ('.bmp', '.jpg', 'jpeg', '.tif'), "Image format is not supported."
            except Exception as e:
                self.label_bamboo.clear()
                self.warning_message_box(str(e))
                return

            try:
                cross_section = QPixmap(url[0])
                cross_section = cross_section.scaled(500, 500)
                if cross_section.isNull():
                    self.label_bamboo.clear()
                    self.warning_message_box("Unable to open input file.  \n\n")
                else:
                    self.label_bamboo.clear()
                    self.label_bamboo.setPixmap(cross_section)
                    self.lineEdit_imagePath.setText(url[0])
                    in_data['img_path'] = url[0]
            except Exception as e:
                self.label_bamboo.clear()
                self.warning_message_box("Unable to open input file, verify  \n file path or image file type.\n\n")

    def browse_folder(self):
        """
        Function mapped to the browse button used to select the folder path that will be used to
        save the intermediate step for the fiber density calculation.
        :return: None
        """
        url = QFileDialog.getExistingDirectory(self, "Open a directory", "", QFileDialog.ShowDirsOnly)
        if url is not '':
            self.lineEdit_intermediateStepPath.setText(url)
            in_data['intermediate_path'] = url

    def disable_dashboard(self):
        """
        Disables the dashboard and hides the graphs.
        :return: None
        """
        self.dashboard_tab.setEnabled(False)
        self.tabWidget_2.setEnabled(False)
        self.graphs_tab.setEnabled(False)
        self.region_density_tab.setEnabled(False)
        self.measurement_data_tab.setEnabled(False)

        self.tabWidget_2.setCurrentIndex(0)

        self.widget_rings.hide()
        self.widget_wedges.hide()
        self.comboBox_rings.hide()
        self.comboBox_wedges.hide()

    def update_num_diameter_measurements(self):
        """
        Update the value for the total number of measurements in the user interface main screen that serves as feedback.
        :return: None
        """
        global in_data

        if self.is_int_inbound(self.lineEdit_numMeasurements.text(), 3, 100):
            temp = int(self.lineEdit_numMeasurements.text()) * 4
            self.label_numMeasurementsFeedback.setText("Total Diameter Measurements: " + str(temp))
            in_data['num_measurement'] = temp
        else:
            self.lineEdit_numMeasurements.clear()

    def update_num_wedges(self):
        """
        Update the value for number of wedges in the user interface main screen that serves as feedback.
        :return: None
        """
        global in_data, wedge_degree

        if self.is_int_inbound(self.lineEdit_numWedges.text(), 3, 100):
            temp = int(self.lineEdit_numWedges.text()) * 4
            wedge_degree = 360 / temp
            self.label_numWedgesFeedback.setText("Num. Wedges: " + str(temp) + " @ {:.1f}º".format(wedge_degree))
            self.label_numRegionsFeedback.setText("Num. Regions: " + str(temp * int(in_data['num_rings'])))
            in_data['num_wedges'] = temp
        else:
            self.lineEdit_numWedges.clear()

    def update_num_rings(self):
        """
        Update the value for number of rings in the user interface main screen that serves as feedback.
        :return: None
        """
        global in_data

        temp = self.lineEdit_numRings.text()
        if self.is_int_inbound(temp, 1, 25):
            self.label_numRingsFeedback.setText("Num. Rings: " + str(temp))
            self.label_numRegionsFeedback.setText("Num. Regions: " + str(int(temp) * int(in_data['num_wedges'])))
            in_data['num_rings'] = int(temp)
        else:
            self.lineEdit_numRings.clear()

    def update_image_dpi(self):
        """
        Update the value for the image dpi (dot per inch) in the user interface main screen that serves as feedback.
        :return: None
        """
        global in_data

        if self.is_int_inbound(self.lineEdit_imageDPI.text(), 1200, 4800):
            temp = int(self.lineEdit_imageDPI.text())
            self.label_imageDPIFeedback.setText("Image DPI: " + str(temp))
            in_data['img_dpi'] = temp
        else:
            self.lineEdit_imageDPI.clear()

    def start_button_func(self):
        """
        Sets what the start button will do when it is pressed. It starts the fiber density calculation and
        disables user input. The button is replaced by the stop button.
        :return: None
        """
        global in_data, debounce
        # if program has not started
        if debounce is not 0:
            if (time.time() - debounce) < .30:
                return
            debounce = 0

        # Test input data for being empty
        if(self.lineEdit_imagePath.text() is "" or self.lineEdit_intermediateStepPath.text() is "" or
                self.lineEdit_numWedges.text() is "" or self.lineEdit_numRings.text() is "" or
                self.lineEdit_numMeasurements.text() is "" or self.lineEdit_imageDPI.text() is ""):
            self.warning_message_box("Make sure all inputs are filled in.")
            return

        # Test numeric input
        if not self.is_int_inbound(self.lineEdit_numMeasurements.text(), 3, 100, self.label_numMeasurements.text()):
            return
        if not self.is_int_inbound(self.lineEdit_numWedges.text(), 3, 100, self.label_numWedges.text()):
            return
        if not self.is_int_inbound(self.lineEdit_numRings.text(), 1, 25, self.label_numRings.text()):
            return
        if not self.is_int_inbound(self.lineEdit_imageDPI.text(), 1200, 4800, self.label_imageDPI.text()):
            return

        # After all inputs have been validated
        self.disable_dashboard()

        # Save input data in in_data dictionary
        in_data['units'] = self.comboBox_units.currentText()
        in_data['num_measurement'] = int(self.lineEdit_numMeasurements.text())*4
        in_data['img_dpi'] = int(self.lineEdit_imageDPI.text())
        in_data['enhance'] = bool(self.checkBox_imageEnhancement.isChecked())
        in_data['pixelMap'] = bool(self.checkBox_pixelMap.isChecked())

        self.inputs_set_enabled(False)

        self.progressBar.show()
        self.label_progressBar.show()
        self.progressBar.setValue(1)

        self.stop_button.setEnabled(True)
        self.start_button.hide()

        # Start Sprout Controller for fiber density calculation
        self.myThread = Sprout.SproutController(self, in_data)
        try:
            self.myThread.start()
            self.myThread.progress.connect(self.progressBar.setValue)
        except:
            self.warning_message_box("Error while starting process.")

    def stop_button_func(self):
        """
        Sets what the stop button will do when it is pressed or is called when the fiber density calculation
        is completed. It cancels the running session and enables user input. If running session is completed it
        enables the user input, and proceeds to display the dashboard (graphs, region density table, and measurement
         data). The button is replaced by the start button.
        :return: None
        """
        global debounce
        # if program is currently in progress

        self.stop_button.setEnabled(False)
        self.stop_button.repaint()

        if not self.myThread.isFinished():
            self.myThread.requestInterruption()
            self.myThread.wait()

        if self.progressBar.value() == 100:
            # if finished successfully

            # self.progressBar.setValue(100)
            self.dashboard_tab.setEnabled(True)
            self.tabWidget_2.setEnabled(True)
            self.graphs_tab.setEnabled(True)
            self.region_density_tab.setEnabled(True)
            self.measurement_data_tab.setEnabled(True)

            # create graphs
            self.create_graphs()

            # create table
            self.create_table()

            # set measurement data
            self.display_measurement_data()

            self.tabWidget_1.setCurrentIndex(1)
            self.tabWidget_2.setCurrentIndex(0)

        self.inputs_set_enabled(True)

        self.progressBar.hide()
        self.label_progressBar.hide()
        self.progressBar.setValue(0)

        self.start_button.show()
        debounce = time.time()

    def inputs_set_enabled(self, val: bool):
        """
        Enable or disable the options presented in the home screen depending on input parameter: val.
        :param val: True to anabel all the options in the home screen and False to disable.
        :return: None
        """
        self.browse_button_1.setEnabled(val)
        self.browse_button_2.setEnabled(val)
        self.comboBox_units.setEnabled(val)
        self.lineEdit_numMeasurements.setEnabled(val)
        self.lineEdit_numWedges.setEnabled(val)
        self.lineEdit_numRings.setEnabled(val)
        self.lineEdit_imageDPI.setEnabled(val)
        self.checkBox_imageEnhancement.setEnabled(val)
        self.checkBox_pixelMap.setEnabled(val)

        self.label_imagePath.setEnabled(val)
        self.label_intermediateStepPath.setEnabled(val)
        self.label_numMeasurements.setEnabled(val)
        self.label_numWedges.setEnabled(val)
        self.label_numRings.setEnabled(val)
        self.label_imageDPI.setEnabled(val)
        self.label_units.setEnabled(val)

    def create_graphs(self):
        """
        Creates the graphs that will be displayed int the dashboard's Graphs tab.
        :return: None
        """
        global default_comboBox_graph_item_count

        # Set Graphs ComboBox
        for x in range(self.comboBox_rings.count()):
            self.comboBox_rings.removeItem(default_comboBox_graph_item_count)

        for x in range(self.comboBox_wedges.count()):
            self.comboBox_wedges.removeItem(default_comboBox_graph_item_count)

        self.comboBox_rings.setCurrentIndex(0)
        self.comboBox_wedges.setCurrentIndex(0)

        # Ring Graph
        self.ring_chart = QChart()

        for x in range(len(self.densities)):
            ring_series = QLineSeries()
            for y in range(len(self.densities[x])-1):
                ring_series.append(y+1, self.densities[x][y])
            self.ring_chart.addSeries(ring_series)
            if x < len(self.densities)-1:
                self.comboBox_rings.addItem("Ring " + str(x+1))

        self.ring_chart.setTitle('Fiber Density VS Wedges')
        self.ring_chart.legend().hide()
        self.ring_chart.createDefaultAxes()
        self.ring_chart.axes(Qt.Horizontal)[0].setRange(1, len(self.densities[0])-1)
        self.ring_chart.axes(Qt.Vertical)[0].setRange(0, 1)
        self.ring_chart.axes(Qt.Horizontal)[0].setTitleText("Wedge Number")
        self.ring_chart.axes(Qt.Vertical)[0].setTitleText("Fiber Density")

        self.chartView = QChartView(self.ring_chart, self.widget_rings)
        self.chartView.resize(self.widget_rings.size())

        # Wedges Graph
        self.wedge_chart = QChart()

        for y in range(len(self.densities[0])):
            if in_data['num_rings'] == 1:
                ring_series = QScatterSeries()
            else:
                ring_series = QLineSeries()
            for x in range(len(self.densities)-1):
                ring_series.append(x+1, self.densities[x][y])
            self.wedge_chart.addSeries(ring_series)
            if y < len(self.densities[0])-1:
                self.comboBox_wedges.addItem("Wedge " + str(y+1))

        self.wedge_chart.setTitle('Fiber Density VS Rings')
        self.wedge_chart.legend().hide()
        self.wedge_chart.createDefaultAxes()
        if (len(self.densities)) == 2:
            self.wedge_chart.axes(Qt.Horizontal)[0].setRange(0, 2)
        else:
            self.wedge_chart.axes(Qt.Horizontal)[0].setRange(1, len(self.densities)-1)
        self.wedge_chart.axes(Qt.Vertical)[0].setRange(0, 1)
        self.wedge_chart.axes(Qt.Horizontal)[0].setTitleText("Ring Number")
        self.wedge_chart.axes(Qt.Vertical)[0].setTitleText("Fiber Density")

        self.chartView = QChartView(self.wedge_chart, self.widget_wedges)
        self.chartView.resize(self.widget_wedges.size())

        self.widget_rings.show()
        self.widget_wedges.show()

        if in_data['num_rings'] == 1:
            self.comboBox_rings.hide()
        else:
            self.comboBox_rings.show()
        self.comboBox_wedges.show()

    def filter_rings_graph(self):
        """
        Filters the rings graph by: All(includes average), All Wedges(only rings are shown),
        Average(only average is shown), and individual rings(varies depending on number of rings).
        :return: None
        """
        global default_comboBox_graph_item_count

        for x in range(len(self.ring_chart.series())):
            self.ring_chart.series()[x].show()

        if self.comboBox_rings.currentText() == "All Rings":
            for x in range(len(self.ring_chart.series()) - 1):
                self.ring_chart.series()[x].show()
            self.ring_chart.series()[len(self.ring_chart.series()) - 1].hide()
        elif self.comboBox_rings.currentText() == "Average":
            for x in range(len(self.ring_chart.series()) - 1):
                self.ring_chart.series()[x].hide()
            self.ring_chart.series()[len(self.ring_chart.series()) - 1].show()
        elif "Ring" in self.comboBox_rings.currentText():
            for x in range(len(self.ring_chart.series())):
                self.ring_chart.series()[x].hide()
            self.ring_chart.series()[self.comboBox_rings.currentIndex() - default_comboBox_graph_item_count].show()

    def filter_wedges_graph(self):
        """
        Filters the wedges graph by: All(includes average), All Wedges(only wedges are shown),
        Average(only average is shown), and individual wedges(varies depending on number of wedges).
        :return: None
        """
        global default_comboBox_graph_item_count

        for x in range(len(self.wedge_chart.series())):
            self.wedge_chart.series()[x].show()

        if self.comboBox_wedges.currentText() == "All Wedges":
            for x in range(len(self.wedge_chart.series()) - 1):
                self.wedge_chart.series()[x].show()
            self.wedge_chart.series()[len(self.wedge_chart.series()) - 1].hide()
        elif self.comboBox_wedges.currentText() == "Average":
            for x in range(len(self.wedge_chart.series()) - 1):
                self.wedge_chart.series()[x].hide()
            self.wedge_chart.series()[len(self.wedge_chart.series()) - 1].show()
        elif "Wedge" in self.comboBox_wedges.currentText():
            for x in range(len(self.wedge_chart.series())):
                self.wedge_chart.series()[x].hide()
            self.wedge_chart.series()[self.comboBox_wedges.currentIndex() - default_comboBox_graph_item_count].show()

    def create_table(self):
        """
        Creates the table that will be presented in the dashboard's Region Density tab
        based on fiber density calculations.
        :return: None
        """
        i = 0
        j = 0
        column_name = []
        row_name = []

        self.tableWidget.setRowCount(len(self.densities))
        self.tableWidget.setColumnCount(len(self.densities[0]))

        for ring in self.densities:
            for wedge in ring:
                item = QTableWidgetItem("{:.4f}".format(wedge))
                item.setTextAlignment(Qt.AlignCenter)

                # if average of average
                if j == (len(self.densities[0])-1) and i == (len(self.densities)-1):
                    font = QFont()
                    font.setBold(True)
                    item.setFont(font)

                self.tableWidget.setItem(i, j, item)
                j += 1
            j = 0
            i += 1

        for x in range(len(self.densities[0])):
            if x == len(self.densities[0]) - 1:
                column_name.append("Average")
            else:
                column_name.append("Wedge " + str(x + 1))
        for y in range(len(self.densities)):
            if y == len(self.densities) - 1:
                row_name.append("Average")
            else:
                row_name.append("Ring " + str(y + 1))

        self.tableWidget.setHorizontalHeaderLabels(column_name)
        self.tableWidget.setVerticalHeaderLabels(row_name)
        self.tableWidget.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers)

    def display_measurement_data(self):
        """
        Manages all output data related to the measurement data that is presented in the dashboard Measurement Data.
        :return: None
        """
        self.lineEdit_area.setText(str("{:.4f}".format(self.measurement_data[0])) + " " + in_data['units'] + "^2")
        self.lineEdit_avgOuterDiameter.setText(str("{:.4f}".format(self.measurement_data[1])) + " " + in_data['units'])
        self.lineEdit_avgInnerDiameter.setText(str("{:.4f}".format(self.measurement_data[2])) + " " + in_data['units'])
        self.lineEdit_AverageT.setText(str("{:.4f}".format(self.measurement_data[3])) + " " + in_data['units'])
        self.lineEdit_centroid_x.setText(str("{:.4f}".format(self.measurement_data[4])) + " " + in_data['units'])
        self.lineEdit_centroid_y.setText(str("{:.4f}".format(self.measurement_data[5])) + " " + in_data['units'])
        self.lineEdit_momentOfInertia_x.setText(str("{:.4f}".format(self.measurement_data[6])) + " " +
                                                in_data['units'] + "^4")
        self.lineEdit_momentOfInertia_y.setText(str("{:.4f}".format(self.measurement_data[7])) + " " +
                                                in_data['units'] + "^4")
        self.lineEdit_productOfInertia.setText(str("{:.4f}".format(self.measurement_data[8])) + " " +
                                               (in_data['units']) + "^4")

    def is_int_inbound(self, ui_in: str, lower: int, upper: int, ui_in_name: str = None):
        """
        Test if user input is in specified upper and lower bound, including upper and lower bound value.
        :param ui_in: user input for value that will be tested
        :param lower: lowest value that ui_in can have to return True
        :param upper: highest value that ui_in can hava to return True
        :param ui_in_name: user input label name that is used in popup messages for users to relate error
        :return: True if ui_in is in upper and lower bound (lower <= ui_in <= upper)
                    otherwise False
        """
        if not (str.isdigit(ui_in)) or int(ui_in) > upper or int(ui_in) < lower:
            if ui_in_name is not None:
                self.warning_message_box(str(ui_in_name) + "\nPlease input a number from "
                                         + str(lower) + " to " + str(upper))
            return False
        else:
            return True

    def warning_message_box(self, message):
        """
        Display a popup message box to inform users of error.
        :param message: Message to be displayed in the popup message
        :return:
        """
        mbox = QMessageBox.critical(self, "Warning!!", message)
        if mbox == QMessageBox.Ok:
            self.lineEdit_numMeasurements.setFocus(0)

    def show_save_files(self):
        """
        Call to displays the popup for the Save Popup Window.
        :return: None
        """
        self.windowModality()
        self.save_window_ui.lineEdit_filePath.setText(os.getcwd())
        self.save_window_ui.show()
        self.save_window_ui.raise_()

    def progress_change(self):
        """
        Signals the user interface when process is completed or wen error occurs.
        :return: None
        """
        if self.progressBar.value() == 2:
            self.stop_button_func()
            self.warning_message_box(str(self.error_message))
        elif self.progressBar.value() == 100:
            self.densities = DM.get_fiber_density_average()
            self.measurement_data = DM.get_dimensional_measurements()
            self.stop_button_func()
Exemple #13
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('Температура')