示例#1
0
    def load_glycation(self, filename: Optional[str] = None) -> None:
        """
        Load glycation data from a CSV file and display it
        in the corresponding chart view.

        :param str filename: directly load this file
        :return: nothing, sets self.se_glycation
        :rtype: None
        """

        # load and clean glycation data
        if filename is None:
            filename, self.last_path = get_filename(
                self, "open", self.tr("Load glycation data ..."),
                self.last_path, FileTypes(["csv"]))
            if filename is None:
                return

        logging.info(
            self.tr("Loading glycation data in '{}'").format(filename))
        try:
            self.glycation = read_clean_datasets(filename)
        except (OSError, ValueError) as e:
            logging.error(str(e))
            QMessageBox.critical(self, self.tr("Error"), str(e))
            return

        # extract x- and y-values from series
        x_values = [str(i) for i in self.glycation.index]
        y_values = [a.nominal_value for a in self.glycation]

        # assemble the chart
        bar_set = QBarSet("glycation abundance")
        bar_set.append(y_values)
        bar_set.setColor(QColor("#a1dab4"))
        bar_set.hovered.connect(self.update_glycation_label)
        bar_series = QBarSeries()
        bar_series.append(bar_set)

        x_axis = QBarCategoryAxis()
        x_axis.append(x_values)
        x_axis.setTitleText(self.tr("count"))

        y_axis = QValueAxis()
        y_axis.setRange(0, 100)
        y_axis.setTitleText(self.tr("abundance"))
        y_axis.setLabelFormat("%d")

        chart = QChart()
        chart.addSeries(bar_series)
        chart.setAxisX(x_axis, bar_series)
        chart.setAxisY(y_axis, bar_series)
        chart.legend().setVisible(False)
        chart.setBackgroundRoundness(0)
        chart.layout().setContentsMargins(0, 0, 0, 0)
        chart.setMargins(QMargins(5, 5, 5, 5))
        self.cvGlycation.setChart(chart)
示例#2
0
class VLineChartView(QChartView):
    def __init__(self):
        super(VLineChartView, self).__init__()
        self.stocks = read_tick_data()
        self.category = [
            trade_date[4:] for trade_date in self.stocks['trade_date']
        ]
        self.resize(800, 300)
        self.initChart()

    def initChart(self):
        self._chart = QChart(title='数量')
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        series = QStackedBarSeries()
        series.setName('数量')
        bar_red = QBarSet('red')
        bar_red.setColor(Qt.red)
        bar_green = QBarSet('green')
        bar_green.setColor(Qt.green)
        for _, stock in self.stocks.iterrows():
            if stock['open'] < stock['close']:
                bar_red.append(stock['vol'] / 100)
                bar_green.append(0)
            else:
                bar_red.append(0)
                bar_green.append(stock['vol'] / 100)

        series.append(bar_red)
        series.append(bar_green)
        self._chart.addSeries(series)
        self._chart.createDefaultAxes()
        self._chart.setLocalizeNumbers(True)
        axis_x = self._chart.axisX()
        axis_y = self._chart.axisY()
        axis_x.setGridLineVisible(False)
        axis_y.setGridLineVisible(False)
        axis_y.setLabelFormat("%.2f")
        axis_x.setCategories(self.category)
        max_p = self.stocks[[
            'vol',
        ]].stack().max() / 100 + 10
        min_p = self.stocks[[
            'vol',
        ]].stack().min() / 100 - 10
        axis_y.setRange(min_p, max_p)

        # chart的图例
        legend = self._chart.legend()
        legend.hide()
        # 设置图例由Series来决定样式
        # legend.setMarkerShape(QLegend.MarkerShapeFromSeries)

        self.setChart(self._chart)
        self._chart.layout().setContentsMargins(0, 0, 0, 0)
        # self._chart.setMargins(QMargins(0, 0, 0, 0))
        self._chart.setBackgroundRoundness(0)
示例#3
0
class pieChartView(QChartView):
    def __init__(self, *args, **kwargs):
        super(pieChartView, self).__init__(*args, **kwargs)
        self.initChart()
        self.refresh()

    def initChart(self):
        self._chart = QChart()
        # 调整边距
        self._chart.layout().setContentsMargins(0, 0, 0, 0)  # 外界
        self._chart.setMargins(QMargins(3, 0, 3, 0))  # 内界
        self._chart.setBackgroundRoundness(0)
        self._chart.setBackgroundVisible(False)
        # 设置主题
        self._chart.setTheme(QChart.ChartThemeBlueIcy)
        # 抗锯齿
        self.setRenderHint(QPainter.Antialiasing)
        # 开启动画效果
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        self._series = QPieSeries()
        self._series.setPieSize(0.8)
        self._chart.addSeries(self._series)
        self.setChart(self._chart)

    def refresh(self):
        # 提示widget
        self.toolTipWidget = GraphicsProxyWidget(self._chart)

    def mouseMoveEvent(self, event):
        super(pieChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        x = pos.x()
        y = pos.y()
        # 得到在坐标系中的所有区域
        self.min_x, self.max_x = self._chart.geometry().width(
        ) * 0.2, self._chart.geometry().width() * 0.8
        self.min_y, self.max_y = self._chart.geometry().height(
        ) * 0.2, self._chart.geometry().height() * 0.9
        serie = self._chart.series()[0]
        slices = [(slice, slice.value()) for slice in serie.slices()]
        if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y:
            t_width = self.toolTipWidget.width()
            t_height = self.toolTipWidget.height()
            title = "数据组成"
            # 如果鼠标位置离右侧的距离小于tip宽度
            x = pos.x() - t_width if self.width() - \
                                     pos.x() - 20 < t_width else pos.x()
            # 如果鼠标位置离底部的高度小于tip高度
            y = pos.y() - t_height if self.height() - \
                                      pos.y() - 20 < t_height else pos.y()
            self.toolTipWidget.show(title, slices, QPoint(x, y))
        else:
            self.toolTipWidget.hide()
示例#4
0
    def agg_glycoforms(self) -> None:
        """
        Display glycoform data in the corresponding chart view.

        :return: nothing
        :rtype: None
        """

        # aggregate "other" abundances
        if self.cbAggGlycoforms.isChecked():
            agg_abundance = (
                self.glycoforms.iloc[self.sbAggGlycoforms.value():].sum())
            self.glycoforms_agg = (
                self.glycoforms.iloc[:self.sbAggGlycoforms.value()].append(
                    pd.Series(agg_abundance, index=[self.tr("other")])))
        else:
            self.glycoforms_agg = self.glycoforms

        # extract x- and y-values from series
        x_values = [str(i) for i in self.glycoforms_agg.index]
        y_values = [a.nominal_value for a in self.glycoforms_agg]

        # assemble the chart
        bar_set = QBarSet("glycoform abundance")
        bar_set.append(y_values)
        bar_set.setColor(QColor("#2c7fb8"))
        bar_set.hovered.connect(self.update_glycoform_label)
        bar_series = QBarSeries()
        bar_series.append(bar_set)

        x_axis = QBarCategoryAxis()
        x_axis.append(x_values)
        x_axis.setTitleVisible(False)
        x_axis.setLabelsAngle(270)

        range_max = max(self.glycoforms_agg).nominal_value
        range_max = math.ceil(range_max / 20) * 20
        tick_count = range_max // 20 + 1
        y_axis = QValueAxis()
        y_axis.setRange(0, range_max)
        y_axis.setTickCount(tick_count)
        y_axis.setTitleText(self.tr("abundance"))
        y_axis.setLabelFormat("%d")

        chart = QChart()
        chart.addSeries(bar_series)
        chart.setAxisX(x_axis, bar_series)
        chart.setAxisY(y_axis, bar_series)
        chart.legend().setVisible(False)
        chart.setBackgroundRoundness(0)
        chart.layout().setContentsMargins(0, 0, 0, 0)
        chart.setMargins(QMargins(5, 5, 5, 5))
        self.cvGlycoforms.setChart(chart)
示例#5
0
    def _fill_chart(self, items: List[Run]):
        series = QLineSeries()
        series.setPointsVisible(True)
        series.setPointLabelsVisible(True)
        series.setPointLabelsFormat("@yPoint")
        series.hovered.connect(self.chart_view.show_series_tooltip)

        self.timestamp_by_run.clear()

        for run in items:
            date_value = calendar.timegm(run.date.timetuple()) * 1000
            total_issues = run.get_total_issues()
            series.append(date_value, total_issues)

            self.timestamp_by_run[date_value] = run

        chart = QChart()
        chart.setTheme(QChart.ChartThemeDark)
        chart.setAnimationOptions(QChart.SeriesAnimations)
        chart.addSeries(series)
        chart.legend().hide()

        # No margin
        chart.layout().setContentsMargins(0, 0, 0, 0)
        chart.setBackgroundRoundness(0)

        axisX = QDateTimeAxis()
        axisX.setFormat("dd/MM/yyyy")
        axisX.setTitleText('Date')
        chart.addAxis(axisX, Qt.AlignBottom)
        series.attachAxis(axisX)

        axisY = QValueAxis()
        axisY.setLabelFormat('%d')
        axisY.setTitleText('Total issues')
        chart.addAxis(axisY, Qt.AlignLeft)
        series.attachAxis(axisY)

        self.chart_view.clear_all_tooltips()
        self.chart_view.setChart(chart)
示例#6
0
class barChartView(QChartView):
    def __init__(self, xAxis=[], *args, **kwargs):
        super(barChartView, self).__init__(*args, **kwargs)
        self.initChart(xAxis)

        # line 宽度需要调整
        self.lineItem = QGraphicsLineItem(self._chart)
        pen = QPen(Qt.gray)
        self.lineItem.setPen(pen)
        self.lineItem.setZValue(998)
        self.lineItem.hide()
        self.cal()

    # 一些固定计算,减少mouseMoveEvent中的计算量
    def cal(self):
        # 提示widget
        self.toolTipWidget = GraphicsProxyWidget(self._chart)
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        self.category_len = len(axisX.categories())
        self.min_x, self.max_x = -0.5, self.category_len - 0.5
        self.min_y, self.max_y = axisY.min(), axisY.max()
        # 坐标系中左上角顶点
        self.point_top = self._chart.mapToPosition(
            QPointF(self.min_x, self.max_y))

    def setCat(self, data):
        self.categories = data

    #初始化
    def initChart(self, xAxis):
        self._chart = QChart()
        # 调整边距
        self._chart.layout().setContentsMargins(0, 0, 0, 0)  # 外界
        self._chart.setMargins(QMargins(3, 0, 3, 0))  # 内界
        self._chart.setBackgroundRoundness(0)
        self._chart.setBackgroundVisible(False)
        # 设置主题
        self._chart.setTheme(QChart.ChartThemeBlueIcy)
        # 抗锯齿
        self.setRenderHint(QPainter.Antialiasing)
        # 开启动画效果
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        self.categories = xAxis
        self._series = QBarSeries(self._chart)
        self._chart.addSeries(self._series)
        self._chart.createDefaultAxes()  # 创建默认的轴
        self._axis_x = QBarCategoryAxis(self._chart)
        self._axis_x.append(self.categories)
        self._axis_y = QValueAxis(self._chart)
        self._axis_y.setTitleText("任务数")
        self._axis_y.setRange(0, 10)
        self._chart.setAxisX(self._axis_x, self._series)
        self._chart.setAxisY(self._axis_y, self._series)
        # chart的图例
        legend = self._chart.legend()
        legend.setVisible(True)

        self.setChart(self._chart)

    def mouseMoveEvent(self, event):
        super(barChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(pos).x()
        y = self._chart.mapToValue(pos).y()
        index = round(x)
        # 得到在坐标系中的所有bar的类型和点
        serie = self._chart.series()[0]
        bars = [
            (bar, bar.at(index)) for bar in serie.barSets()
            if self.min_x <= x <= self.max_x and self.min_y <= y <= self.max_y
        ]
        # print(bars)
        if bars:
            right_top = self._chart.mapToPosition(
                QPointF(self.max_x, self.max_y))
            # 等分距离比例
            step_x = round(
                (right_top.x() - self.point_top.x()) / self.category_len)
            posx = self._chart.mapToPosition(QPointF(x, self.min_y))
            self.lineItem.setLine(posx.x(), self.point_top.y(), posx.x(),
                                  posx.y())
            self.lineItem.show()
            try:
                title = self.categories[index]
            except:
                title = ""
            t_width = self.toolTipWidget.width()
            t_height = self.toolTipWidget.height()
            # 如果鼠标位置离右侧的距离小于tip宽度
            x = pos.x() - t_width if self.width() - \
                pos.x() - 20 < t_width else pos.x()
            # 如果鼠标位置离底部的高度小于tip高度
            y = pos.y() - t_height if self.height() - \
                pos.y() - 20 < t_height else pos.y()
            self.toolTipWidget.show(title, bars, QPoint(x, y))
        else:
            self.toolTipWidget.hide()
            self.lineItem.hide()
示例#7
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)
示例#8
0
class Ui(mainwindow.Ui_MainWindow):
    def __init__(self, MainWindow):
        #super(Ui, self).__init__(M)
        super(Ui, self).setupUi(MainWindow)
        #uic.loadUi('UI/mainwindow.ui', self)

        self.MainWindow = MainWindow

        self.initialize()

    def close(self):
        self.MainWindow.close()

    def style(self):
        return self.MainWindow.style()

    def initialize(self):
        self.tabWidget.setCurrentIndex(0)
        self.actionExit.triggered.connect(self.close)
        self.action_Plot.setEnabled(False)

        self.actionNext.setIcon(
            self.style().standardIcon(
                QtWidgets.QStyle.SP_ArrowForward))
        self.actionPrevious.setIcon(
            self.style().standardIcon(
                QtWidgets.QStyle.SP_ArrowBack))
        self.action_Open.setIcon(
            self.style().standardIcon(
                QtWidgets.QStyle.SP_DialogOpenButton))
        self.actionSave.setIcon(
            self.style().standardIcon(
                QtWidgets.QStyle.SP_DriveFDIcon))

        self.actionSave.triggered.connect(self.save)

        self.action_Open.triggered.connect(self.getOpenFilename)

        self.actionNext.triggered.connect(self.nextPacket)
        self.actionPrevious.triggered.connect(self.previousPacket)
        self.actionAbout.triggered.connect(self.about)

        self.actionPrevious.setEnabled(False)
        self.actionNext.setEnabled(False)
        self.actionSave.setEnabled(False)
        self.action_Plot.setEnabled(False)
        self.actionPaste.triggered.connect(self.onPasteTriggered)
        # self.actionLog.triggered.connect(self.dockWidget_2.show)
        self.actionSet_IDB.triggered.connect(self.onSetIDBClicked)
        self.plotButton.clicked.connect(self.onPlotButtonClicked)
        self.exportButton.clicked.connect(self.onExportButtonClicked)
        self.action_Plot.triggered.connect(self.onPlotActionClicked)
        self.actionLoad_mongodb.triggered.connect(self.onLoadMongoDBTriggered)
        self.mdb=None

        self.current_row = 0
        self.data=[]
        self.x=[]
        self.y=[]
        self.xlabel='x'
        self.ylabel='y'

        self.chart = QChart()
        self.chart.layout().setContentsMargins(0,0,0,0)
        self.chart.setBackgroundRoundness(0)
        self.savePlotButton.clicked.connect(self.savePlot)
        
        self.chartView = QChartView(self.chart)
        self.gridLayout.addWidget(self.chartView, 1, 0, 1, 15)

        # IDB location

        self.settings = QtCore.QSettings('FHNW', 'stix_parser')
    def onExportButtonClicked(self):
        if self.y:
            filename = str(QtWidgets.QFileDialog.getSaveFileName(
                None, "Save file", "", "*.csv")[0])
            if filename:
                with open(filename,'w') as f:
                    f.write('{},{}\n'.format(self.xlabel,self.ylabel))
                    for xx,yy in zip(self.x,self.y):
                        f.write('{},{}\n'.format(xx,yy))
                    self.showMessage('The data has been written to {}'.format(filename))
        else:
            msgBox = QtWidgets.QMessageBox()
            msgBox.setIcon(QtWidgets.QMessageBox.Information)
            msgBox.setText('Plot first!')
            msgBox.setWindowTitle("Warning")
            msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
            msgBox.exec_()


    def savePlot(self):
        #if self.figure.get_axes():
        if self.chart:
            filename = str(QtWidgets.QFileDialog.getSaveFileName(
                None, "Save file", "", "*.png *.jpg")[0])
            if filename:
                if not filename.endswith(('.png','.jpg')):
                    filename+='.png'
                #self.figure.savefig(filename)
                p=self.chartView.grab()
                p.save(filename)
                self.showMessage(('Saved to %s.' % filename))


        else:
            msgBox = QtWidgets.QMessageBox()
            msgBox.setIcon(QtWidgets.QMessageBox.Information)
            msgBox.setText('The canvas is empty!')
            msgBox.setWindowTitle("STIX DATA VIEWER")
            msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
            msgBox.exec_()

    def onPasteTriggered(self):
        pass

    def showMessageBox(self, message, content):
        msg = QtWidgets.QMessageBox()
        msg.setIcon(QtWidgets.QMessageBox.Critical)
        msg.setText("Error")
        msg.setInformativeText(message)
        msg.setWindowTitle("Error")
        msg.setDetailedText(content)
        msg.setStandardButtons(QtWidgets.QMessageBox.Ok |
                               QtWidgets.QMessageBox.Cancel)
        retval = msg.exec_()

    def showMessage(self, msg):
        # if destination != 1:
        self.statusbar.showMessage(msg)
        # if destination !=0 :
        #    self.listWidget_2.addItem(msg)

    def onSetIDBClicked(self):
        pass
    def save(self):
        pass

    def setListViewSelected(self, row):
        #index = self.model.createIndex(row, 0);
        # if index.isValid():
        #    self.model.selectionModel().select( index, QtGui.QItemSelectionModel.Select)
        pass

    def about(self):
        msgBox = QtWidgets.QMessageBox()
        msgBox.setIcon(QtWidgets.QMessageBox.Information)
        msgBox.setText("STIX raw data parser and viewer, [email protected]")
        msgBox.setWindowTitle("Stix data viewer")
        msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
        msgBox.exec_()

    def nextPacket(self):
        self.current_row += 1
        length=len(self.data)
        
        if self.current_row>=length:
            self.current_row=length-1
            self.showMessage('No more packet!')
            

        self.showPacket(self.current_row)
        self.setListViewSelected(self.current_row)

    def previousPacket(self):
        self.current_row -= 1
        if self.current_row <0:
            self.current_row=0
            self.showMessage('Reach the first packet!')

        self.showPacket(self.current_row)
        self.setListViewSelected(self.current_row)

    def getOpenFilename(self):
        pass

    def openFile(self, filename):
        pass



    def onDataLoaded(self, data,clear=True):
        if not clear:
            self.data.append(data)
        else:
            self.data = data
        self.displayPackets(clear)

        if self.data:
            self.actionPrevious.setEnabled(True)
            self.actionNext.setEnabled(True)
            self.actionSave.setEnabled(True)
            self.action_Plot.setEnabled(True)

    def displayPackets(self,clear=True):
        if clear:
            self.packetTreeWidget.clear()
        t0=0
        for p in self.data:
            if type(p) is not dict:
                continue
            header = p['header']
            root = QtWidgets.QTreeWidgetItem(self.packetTreeWidget)
            if t0==0:
                t0=header['time']

            root.setText(0, '{:.2f}'.format(header['time']-t0))
            root.setText(1,
                         ('TM({},{}) - {}').format(header['service_type'],
                                                   header['service_subtype'],
                                                   header['DESCR']))
        self.total_packets = len(self.data)
        self.showMessage((('%d packets loaded') % (self.total_packets)))
        self.packetTreeWidget.currentItemChanged.connect(self.onPacketSelected)
        self.showPacket(0)

    def onLoadMongoDBTriggered(self):
        diag=QtWidgets.QDialog()
        diag_ui=mongo_dialog.Ui_Dialog()
        diag_ui.setupUi(diag)
        diag_ui.pushButton.setFocus(True)
        #self.settings = QtCore.QSettings('FHNW', 'stix_parser')
        self.mongo_server= self.settings.value('mongo_server', [], str)
        self.mongo_port= self.settings.value('mongo_port', [], str)
        self.mongo_user= self.settings.value('mongo_user', [], str)
        self.mongo_pwd= self.settings.value('mongo_pwd', [], str)

        if self.mongo_server:
            diag_ui.serverLineEdit.setText(self.mongo_server)
        if self.mongo_port:
            diag_ui.portLineEdit.setText(self.mongo_port)

        if self.mongo_user:
            diag_ui.userLineEdit.setText(self.mongo_user)
        if self.mongo_pwd:
            diag_ui.pwdLineEdit.setText(self.mongo_pwd)

        diag_ui.pushButton.clicked.connect(partial(self.loadRunsFromMongoDB,diag_ui))
        diag_ui.buttonBox.accepted.connect(partial(self.loadDataFromMongoDB,diag_ui,diag))
        diag.exec_()
    def loadRunsFromMongoDB(self,dui):
        server=dui.serverLineEdit.text()
        port=dui.portLineEdit.text()
        user=dui.userLineEdit.text()
        pwd=dui.pwdLineEdit.text()

        self.showMessage('saving setting...')
        if self.mongo_server!=server:
            self.settings.setValue('mongo_server', server)
        if self.mongo_port!=port:
            self.settings.setValue('mongo_port', port)

        if self.mongo_user!=user:
            self.settings.setValue('mongo_user', user)
        if self.mongo_pwd!=pwd:
            self.settings.setValue('mongo_pwd', pwd)


        self.showMessage('connecting Mongo database ...')
        self.mdb=mgdb.MongoDB(server,int(port),user,pwd)
        dui.treeWidget.clear()
        self.showMessage('Fetching data...')
        for run in self.mdb.get_runs():
            root = QtWidgets.QTreeWidgetItem(dui.treeWidget)
            root.setText(0, str(run['_id']))
            root.setText(1, run['file'])
            root.setText(2, run['date'])
            root.setText(3, str(run['start']))
            root.setText(4, str(run['end']))
        self.showMessage('Runs loaded!')
    def loadDataFromMongoDB(self,dui,diag):
        selected_runs=[]
        for item in dui.treeWidget.selectedItems():
            selected_runs.append(item.text(0))
        if not selected_runs:
            self.showMessage('Run not selected!')
        if selected_runs:
            diag.done(0)
            self.showMessage('Loading data ...!')
            data=self.mdb.get_packets(selected_runs[0])
            if data:
                self.onDataLoaded(data,clear=True)
            else:
                self.showMessage('No packets found!')
        #close

    def onPacketSelected(self, cur, pre):
        self.current_row = self.packetTreeWidget.currentIndex().row()
        self.showMessage((('Packet #%d selected') % self.current_row))
        self.showPacket(self.current_row)

    def showPacket(self, row):
        if not self.data:
            return
        header = self.data[row]['header']
        self.showMessage(
            (('Packet %d / %d  %s ') %
             (row, self.total_packets, header['DESCR'])))

        self.paramTreeWidget.clear()

        header_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget)
        header_root.setText(0, "Header")
        rows = len(header)
        for key, val in header.items():
            root = QtWidgets.QTreeWidgetItem(header_root)
            root.setText(0, key)
            root.setText(1, str(val))

        params = self.data[row]['parameters']
        param_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget)
        param_root.setText(0, "Parameters")
        self.showParameterTree(params, param_root)
        self.paramTreeWidget.expandItem(param_root)
        self.paramTreeWidget.expandItem(header_root)

    def showParameterTree(self, params, parent):
        for p in params:
            root = QtWidgets.QTreeWidgetItem(parent)
            if not p:
                continue
            try:
                param_name=p['name']
                desc=''
                scos_desc=''
                try:
                    desc=param_desc.PCF[param_name]
                    scos_desc=param_desc.SW[param_name]
                except KeyError:
                    pass
                root.setToolTip(1,scos_desc)
                root.setText(0, param_name)
                root.setText(1, desc)
                root.setText(2, str(p['raw']))
                root.setText(3, str(p['value']))
                if 'child' in p:
                    if p['child']:
                        self.showParameterTree(p['child'], root)
            except KeyError:
                self.showMessage(
                    ('[Error  ]: keyError occurred when adding parameter'))
        self.paramTreeWidget.itemDoubleClicked.connect(self.onTreeItemClicked)

    def walk(self, name, params, header, ret_x, ret_y, xaxis=0, data_type=0):
        if not params:
            return
        timestamp = header['time']
        #parameters=[p for p in params if p['name'] == name] 
        for p in params:
            if type(p) is not dict:
                continue
        #for p in parameters:
            if name == p['name']:
                values = None
                #print('data type:{}'.format(data_type))
                if data_type == 0:
                    values = p['raw']
                else:
                    values = p['value']
                try:
                    yvalue = None
                    if (type(values) is tuple) or (type(values) is list):
                        yvalue = float(values[0])
                    else:
                        yvalue = float(values)
                    ret_y.append(yvalue)
                    if xaxis == 1:
                        ret_x.append(timestamp)
                    else:
                        self.showMessage((('Can not plot %s  ') % str(yvalue)))
                except Exception as e:
                    self.showMessage((('%s ') % str(e)))

            if 'child' in p:
                if p['child']:
                    self.walk(
                        name,
                        p['child'],
                        header,
                        ret_x,
                        ret_y,
                        xaxis,
                        data_type)

    def onPlotButtonClicked(self):
        if self.chart:
            self.chart.removeAllSeries()
        if not self.data:
            return
        self.showMessage('Preparing plot ...')
        name = self.paramNameEdit.text()
        packet_selection = self.comboBox.currentIndex()
        xaxis_type = self.xaxisComboBox.currentIndex()
        data_type = self.dataTypeComboBox.currentIndex()

        timestamp = []
        self.y = []
        packet_id = self.current_row
        params = self.data[packet_id]['parameters']
        header = self.data[packet_id]['header']
        current_spid=header['SPID']
        if packet_selection == 0:
            self.walk(
                name,
                params,
                header,
                timestamp,
                self.y,
                xaxis_type,
                data_type)
        elif packet_selection == 1:
            for packet in self.data:
                header = packet['header']
                if packet['header']['SPID'] != current_spid:
                    continue
                #only look for parameters in the packets of the same type
                params = packet['parameters']
                self.walk(
                    name,
                    params,
                    header,
                    timestamp,
                    self.y,
                    xaxis_type,
                    data_type)

        self.x = []



        if not self.y:
            self.showMessage('No data points')
        elif self.y:
            style = self.styleEdit.text()
            if not style:
                style = '-'
            title = '%s' % str(name)
            desc = self.descLabel.text()
            if desc:
                title += '- %s' % desc

            self.chart.setTitle(title)

            ylabel = 'Raw value'
            xlabel = name
            if data_type == 1:
                ylabel = 'Engineering  value'
            if xaxis_type == 0:
                xlabel = "Packet #"
                self.x = range(0, len(self.y))
            if xaxis_type == 1:
                self.x = [t - timestamp[0] for t in timestamp]
                xlabel = 'Time -T0 (s)'

            #if xaxis_type != 2:
            if True:
                series = QLineSeries()
                series2 = None

                # print(y)
                # print(x)
                for xx, yy in zip(self.x, self.y):
                    series.append(xx, yy)

                if 'o' in style:
                    series2 = QScatterSeries()
                    for xx, yy in zip(self.x, self.y):
                        series2.append(xx, yy)
                    self.chart.addSeries(series2)
                self.chart.addSeries(series)

                self.showMessage('plotted!')

                #self.chart.createDefaultAxes()
                axisX = QValueAxis()
                axisX.setTitleText(xlabel)
                axisY = QValueAxis()
                axisY.setTitleText(ylabel)

                self.chart.setAxisX(axisX)
                self.chart.setAxisY(axisY)
                series.attachAxis(axisX)
                series.attachAxis(axisY)

                # histogram
            #else:
            #    nbins = len(set(self.y))
            #    ycounts, xedges = np.histogram(self.y, bins=nbins)
            #    series = QLineSeries()
            #    for i in range(0, nbins):
            #        meanx = (xedges[i] + xedges[i + 1]) / 2.
            #        series.append(meanx, ycounts[i])
            #    # series.append(dataset)
            #    self.chart.addSeries(series)
            #    #self.chart.createDefaultAxes()
            #    self.showMessage('Histogram plotted!')

            #    axisX = QValueAxis()

            #    axisX.setTitleText(name)
            #    axisY = QValueAxis()

            #    axisY.setTitleText("Counts")

            #    self.chart.setAxisY(axisY)
            #    self.chart.setAxisX(axisX)
            ##    series.attachAxis(axisX)
            #    series.attachAxis(axisY)

            # self.widget.setChart(self.chart)
            self.xlabel=xlabel
            self.ylabel=ylabel
            self.chartView.setRubberBand(QChartView.RectangleRubberBand)
            self.chartView.setRenderHint(QtGui.QPainter.Antialiasing)

    def plotParameter(self, name=None, desc=None):
        self.tabWidget.setCurrentIndex(1)
        if name:
            self.paramNameEdit.setText(name)
        if desc:
            self.descLabel.setText(desc)

    def onPlotActionClicked(self):
        self.tabWidget.setCurrentIndex(1)
        self.plotParameter()

    def onTreeItemClicked(self, it, col):
        #print(it, col, it.text(0))
        self.plotParameter(it.text(0), it.text(1))

    def error(self, msg, description=''):
        self.showMessage((('Error: %s - %s') % (msg, description)))

    def warning(self, msg, description=''):
        self.showMessage((('Warning: %s - %s') % (msg, description)))

    def info(self, msg, description=''):
        self.showMessage((('Info: %s - %s') % (msg, description)))
示例#9
0
class ChartView(QChartView):
    def __init__(self, file, parent=None):
        super(ChartView, self).__init__(parent)
        self._chart = QChart()
        self._chart.setAcceptHoverEvents(True)
        self.setChart(self._chart)
        self.initUi(file)

    def initUi(self, file):
        if isinstance(file, dict):
            return self.__analysis(file)
        if isinstance(file, str):
            if not os.path.isfile(file):
                return self.__analysis(json.loads(file))
            with open(file, "rb") as fp:
                data = fp.read()
                encoding = chardet.detect(data) or {}
                data = data.decode(encoding.get("encoding") or "utf-8")
            self.__analysis(json.loads(data))

    #     def onSeriesHoverd(self, point, state):
    #         print(point, state)

    def mouseMoveEvent(self, event):
        super(ChartView, self).mouseMoveEvent(event)
        # 获取x和y轴的最小最大值
        axisX, axisY = self._chart.axisX(), self._chart.axisY()
        min_x, max_x = axisX.min(), axisX.max()
        min_y, max_y = axisY.min(), axisY.max()
        # 把鼠标位置所在点转换为对应的xy值
        x = self._chart.mapToValue(event.pos()).x()
        y = self._chart.mapToValue(event.pos()).y()
        index = round(x)  # 四舍五入
        print(x, y, index)
        # 得到在坐标系中的所有series的类型和点
        points = [(s.type(), s.at(index)) for s in self._chart.series()
                  if min_x <= x <= max_x and min_y <= y <= max_y]
        print(points)

    def __getColor(self, color=None, default=Qt.white):
        '''
        :param color: int|str|[r,g,b]|[r,g,b,a]
        '''
        if not color:
            return QColor(default)
        if isinstance(color, QBrush):
            return color
        # 比如[r,g,b]或[r,g,b,a]
        if isinstance(color, list) and 3 <= len(color) <= 4:
            return QColor(*color)
        else:
            return QColor(color)

    def __getPen(self,
                 pen=None,
                 default=QPen(Qt.white, 1, Qt.SolidLine, Qt.SquareCap,
                              Qt.BevelJoin)):
        '''
        :param pen: pen json
        '''
        if not pen or not isinstance(pen, dict):
            return default
        return QPen(self.__getColor(pen.get("color", None) or default.color()),
                    pen.get("width", 1) or 1,
                    pen.get("style", 0) or 0,
                    pen.get("capStyle", 16) or 16,
                    pen.get("joinStyle", 64) or 64)

    def __getAlignment(self, alignment):
        '''
        :param alignment: left|top|right|bottom
        '''
        try:
            return getattr(Qt, "Align" + alignment.capitalize())
        except:
            return Qt.AlignTop

    #         if alignment == "left":
    #             return Qt.AlignLeft
    #         if alignment == "right":
    #             return Qt.AlignRight
    #         if alignment == "bottom":
    #             return Qt.AlignBottom
    #         return Qt.AlignTop

    def __setTitle(self, title=None):
        '''
        :param title: title json
        '''
        if not title or not isinstance(title, dict):
            return
        # 设置标题
        self._chart.setTitle(title.get("text", "") or "")
        # 设置标题颜色
        self._chart.setTitleBrush(
            self.__getColor(
                title.get("color", self._chart.titleBrush())
                or self._chart.titleBrush()))
        # 设置标题字体
        font = QFont(title.get("font", "") or self._chart.titleFont())
        pointSize = title.get("pointSize", -1) or -1
        if pointSize > 0:
            font.setPointSize(pointSize)
        font.setWeight(title.get("weight", -1) or -1)
        font.setItalic(title.get("italic", False) or False)
        self._chart.setTitleFont(font)

    def __setAnimation(self, animation=None):
        '''
        :param value: animation json
        '''
        if not animation or not isinstance(animation, dict):
            return
        # 动画持续时间
        self._chart.setAnimationDuration(
            animation.get("duration", 1000) or 1000)
        # 设置动画曲线
        self._chart.setAnimationEasingCurve(
            EasingCurve.get(animation.get("curve", 10) or 10, None)
            or QEasingCurve.OutQuart)
        # 设置开启何种动画
        self._chart.setAnimationOptions(
            AnimationOptions.get(animation.get("options", 0) or 0, None)
            or QChart.NoAnimation)

    def __setBackground(self, background=None):
        '''
        :param background:background json
        '''
        if not background or not isinstance(background, dict):
            return
        # 设置是否背景可用
        self._chart.setBackgroundVisible(
            background.get("visible", True) or True)
        # 设置背景矩形的圆角
        self._chart.setBackgroundRoundness(background.get("radius", 0) or 0)
        # 设置下拉阴影
        self._chart.setDropShadowEnabled(
            background.get("dropShadow", True) or True)
        # 设置pen
        self._chart.setBackgroundPen(
            self.__getPen(background.get("pen", None),
                          self._chart.backgroundPen()))
        # 设置背景
        image = background.get("image", None)
        color = background.get("color", None)
        if image:
            self._chart.setBackgroundBrush(QBrush(QPixmap(image)))
        elif color:
            self._chart.setBackgroundBrush(
                self.__getColor(color, self._chart.backgroundBrush()))

    def __setMargins(self, margins=None):
        '''
        :param margins: margins json
        '''
        if not margins or not isinstance(margins, dict):
            return
        left = margins.get("left", 20) or 20
        top = margins.get("top", 20) or 20
        right = margins.get("right", 20) or 20
        bottom = margins.get("bottom", 20) or 20
        self._chart.setMargins(QMargins(left, top, right, bottom))

    def __setLegend(self, legend=None):
        '''
        :param legend: legend json
        '''
        if not legend or not isinstance(legend, dict):
            return
        _legend = self._chart.legend()
        _legend.setAlignment(self.__getAlignment(legend.get("alignment",
                                                            None)))
        _legend.setShowToolTips(legend.get("showToolTips", True) or True)

    def __getSerie(self, serie=None):
        if not serie or not isinstance(serie, dict):
            return None
        types = serie.get("type", "") or ""
        data = serie.get("data", []) or []
        if not data or not isinstance(data, list):
            return None
        if types == "line":
            _series = QLineSeries(self._chart)
        else:
            return None
        # 设置series名字
        _series.setName(serie.get("name", "") or "")
        # 添加数据到series中
        for index, value in enumerate(data):
            # 保证vlaue必须是数字
            _series.append(index, value if type(value) in (int, float) else 0)
        return _series

    def __setSeries(self, series=None):
        if not series or not isinstance(series, list):
            return
        for serie in series:
            _serie = self.__getSerie(serie)
            if _serie:
                #                 _serie.hovered.connect(self.onSeriesHoverd)
                self._chart.addSeries(_serie)
        # 创建默认的xy轴
        self._chart.createDefaultAxes()

    def __setAxisX(self, axisx=None):
        if not axisx or not isinstance(axisx, dict):
            return
        series = self._chart.series()
        if not series:
            return
        types = axisx.get("type", None)
        data = axisx.get("data", []) or []
        if not data or not isinstance(data, list):
            return None
        minx = self._chart.axisX().min()
        maxx = self._chart.axisX().max()
        if types == "category":
            xaxis = QCategoryAxis(
                self._chart,
                labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue)
            # 隐藏网格
            xaxis.setGridLineVisible(False)
            # 刻度条数
            tickc_d = len(data)
            tickc = tickc_d if tickc_d > 1 else self._chart.axisX().tickCount()
            xaxis.setTickCount(tickc)
            # 强制x轴刻度与新刻度条数一致
            self._chart.axisX().setTickCount(tickc)
            step = (maxx - minx) / (tickc - 1)
            for i in range(min(tickc_d, tickc)):
                xaxis.append(data[i], minx + i * step)
            self._chart.setAxisX(xaxis, series[-1])

    def __analysis(self, datas):
        '''
        analysis json data
        :param datas: json data
        '''
        # 标题
        self.__setTitle(datas.get("title", None))
        # 抗锯齿
        if (datas.get("antialiasing", False) or False):
            self.setRenderHint(QPainter.Antialiasing)
        # 主题
        self._chart.setTheme(datas.get("theme", 0) or 0)
        # 动画
        self.__setAnimation(datas.get("animation", None))
        # 背景设置
        self.__setBackground(datas.get("background", None))
        # 边距设置
        self.__setMargins(datas.get("margins", None))
        # 设置图例
        self.__setLegend(datas.get("legend", None))
        # 设置series
        self.__setSeries(datas.get("series", None))
        # 自定义的x轴
        self.__setAxisX(datas.get("axisx", None))
示例#10
0
class MainWindow(QMainWindow, Ui_MainWindow):
    """
    Main application window
    """
    WIDTH = 150
    HEIGHT = 50
    MARGIN = 5
    max_ping = 0
    max_loss = 0

    def __init__(self, host, history, history_size, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.host = host
        self.history = history
        self.history_size = history_size
        self.setupUi(self)
        self.setWindowFlag(Qt.WindowStaysOnTopHint)
        self.position_to_dock()
        self.series_delay = QLineSeries()
        self.series_loss = QLineSeries()
        self.axis_X = QDateTimeAxis()  # pylint: disable=invalid-name
        self.axis_X.setTickCount(3)
        self.axis_X.setFormat("HH:mm")
        self.axis_X.setTitleText("Time")
        self.chart = QChart()
        self.chart.addSeries(self.series_delay)
        self.chart.addSeries(self.series_loss)
        self.chart.setTitle(f"Connection to {self.host}")
        self.init_series(self.series_delay, "Delay ms")
        self.init_series(self.series_loss, "Loss %")
        self.chart.legend().setVisible(False)
        self.chart.legend().setAlignment(Qt.AlignBottom)
        self.chart.layout().setContentsMargins(0, 0, 0, 0)
        self.chart.setBackgroundRoundness(0)
        self.chart.setMargins(QMargins(0, 0, 0, 0))
        self.chartWidget.setChart(self.chart)
        self.chartWidget.setRenderHint(QPainter.Antialiasing)

    def init_series(self, series, label):
        """
        Series settings
        """
        self.chart.setAxisX(self.axis_X, series)
        axis_Y = QValueAxis()  # pylint: disable=invalid-name
        axis_Y.setLabelFormat("%i")
        axis_Y.setTitleText(label)
        axis_Y.setRange(0, 100)
        self.chart.addAxis(axis_Y, Qt.AlignLeft)
        self.chart.setAxisY(axis_Y, series)

    def add_series(self, ping, loss):
        """
        Append series data
        """
        self.max_ping = max(ping or 0, self.max_ping)
        self.max_loss = max(loss, self.max_loss)
        if self.series_delay.count() > self.history_size:
            self.series_delay.remove(0)
        self.series_delay.append(
            QDateTime.currentDateTime().toMSecsSinceEpoch(), ping or 0)
        if self.series_loss.count() > self.history_size:
            self.series_loss.remove(0)
        self.series_loss.append(
            QDateTime.currentDateTime().toMSecsSinceEpoch(), loss)
        self.axis_X.setRange(
            QDateTime.currentDateTime().addSecs(-self.history),
            QDateTime.currentDateTime())
        self.chart.axisY().setRange(0, self.max_ping + self.MARGIN)

    def position_to_dock(self):
        """
        Adjust main window position according to it's size and desktop
        """
        desktop_geometry = QDesktopWidget().availableGeometry()
        self.setGeometry(
            desktop_geometry.width() - self.width() - self.MARGIN,
            desktop_geometry.height() - self.height() - self.MARGIN,
            self.width(), self.height())

    def set_labels(self, mean_=None, curr=None, loss=None):
        """
        Update window text
        """
        mean_text = curr_text = loss_text = "No connection"
        if mean_ is not None:
            mean_text = f"Mean ping: {mean_}ms"
        if curr is not None:
            curr_text = f"Last ping: {curr}ms"
        if loss is not None:
            loss_text = f"Ping loss: {loss}%"
        self.meanLabel.setText(mean_text)
        self.currLabel.setText(curr_text)
        self.lossLabel.setText(loss_text)

    def toggle(self, reason):
        """
        Toggle window visibility
        """
        if reason == QSystemTrayIcon.ActivationReason.DoubleClick:  # pylint: disable=no-member
            self.setVisible(not self.isVisible())
            if self.isVisible():
                self.activateWindow()
示例#11
0
class KLineChartView(QChartView):

    # QCandlestickSeries的hovered的信号响应后传递日期出去
    candles_hovered = pyqtSignal(bool, str)

    def __init__(self, data: pd.DataFrame):
        super(KLineChartView, self).__init__()
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        self._chart = QChart()
        self._series = QCandlestickSeries()
        self._stocks = data
        self._category = list()
        self._count = None

        self.init_chart()
        self._zero_value = (0, self._chart.axisY().min())
        self._max_value = (len(self._chart.axisX().categories()),
                           self._chart.axisY().max())
        self._zero_point = self._chart.mapToPosition(
            QPointF(self._zero_value[0], self._zero_value[1]))
        self._max_point = self._chart.mapToPosition(
            QPointF(self._max_value[0], self._max_value[1]))
        # 计算x轴单个cate的宽度,用来处理横线不能画到边界
        self._cate_width = (self._max_point.x() - self._zero_point.x()) / len(
            self._category)

        self._series.hovered.connect(self.on_series_hovered)

    def on_series_hovered(self, status, candles_set):
        trade_date = time.strftime('%Y%m%d',
                                   time.localtime(candles_set.timestamp()))
        self.candles_hovered.emit(status, trade_date)

    def set_name(self, name):
        self._series.setName(name)

    def clear_series_values(self):
        self._series.clear()
        self._chart.axisY().setRange(0, 10)
        self._chart.axisX().setCategories(list())
        self._stocks = None

    def add_series_values(self, data: pd.DataFrame, is_init=False):
        self._stocks = data
        self._category = self._stocks['trade_date']
        self._count = len(self._category)
        for _, stock in self._stocks.iterrows():
            time_p = datetime.datetime.strptime(stock['trade_date'], '%Y%m%d')
            time_p = float(time.mktime(time_p.timetuple()))
            _set = QCandlestickSet(float(stock['open']), float(stock['high']),
                                   float(stock['low']), float(stock['close']),
                                   time_p, self._series)
            self._series.append(_set)

        if not is_init:
            self._stocks = data
            self._category = self._stocks['trade_date']
            axis_x = self._chart.axisX()
            axis_y = self._chart.axisY()
            axis_x.setCategories(self._category)
            max_p = self._stocks[['high', 'low']].stack().max()
            min_p = self._stocks[['high', 'low']].stack().min()
            axis_y.setRange(min_p * 0.99, max_p * 1.01)
            self._zero_value = (0, self._chart.axisY().min())
            self._max_value = (len(self._chart.axisX().categories()),
                               self._chart.axisY().max())
            # 计算x轴单个cate的宽度,用来处理横线不能画到边界
            self._cate_width = (self._max_point.x() -
                                self._zero_point.x()) / len(self._category)

    def resizeEvent(self, event):
        super(KLineChartView, self).resizeEvent(event)
        self._zero_point = self._chart.mapToPosition(
            QPointF(self._zero_value[0], self._zero_value[1]))
        self._max_point = self._chart.mapToPosition(
            QPointF(self._max_value[0], self._max_value[1]))
        self._cate_width = (self._max_point.x() - self._zero_point.x()) / len(
            self._category)

    def max_point(self):
        return QPointF(self._max_point.x() + self._cate_width / 2,
                       self._max_point.y())

    def min_point(self):
        return QPointF(self._zero_point.x() - self._cate_width / 2,
                       self._zero_point.y())

    def init_chart(self):
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        self._series.setIncreasingColor(QColor(Qt.red))
        self._series.setDecreasingColor(QColor(Qt.green))
        self._series.setName(self._stocks['name'].iloc[0])
        self.add_series_values(self._stocks, True)
        self._chart.addSeries(self._series)

        self._chart.createDefaultAxes()
        self._chart.setLocalizeNumbers(True)
        axis_x = self._chart.axisX()
        axis_y = self._chart.axisY()
        axis_x.setGridLineVisible(False)
        axis_y.setGridLineVisible(False)
        axis_x.setCategories(self._category)
        axis_x.setLabelsVisible(False)
        axis_x.setVisible(False)
        max_p = self._stocks[['high', 'low']].stack().max()
        min_p = self._stocks[['high', 'low']].stack().min()
        axis_y.setRange(min_p * 0.99, max_p * 1.01)

        # chart的图例
        legend = self._chart.legend()
        # 设置图例由Series来决定样式
        legend.setMarkerShape(QLegend.MarkerShapeFromSeries)

        self.setChart(self._chart)
        # 设置外边界全部为0
        self._chart.layout().setContentsMargins(0, 0, 0, 0)
        # 设置内边界的bottom为0
        margins = self._chart.margins()
        self._chart.setMargins(QMargins(margins.left(), 0, margins.right(), 0))
        # 设置背景区域无圆角
        self._chart.setBackgroundRoundness(0)
示例#12
0
class VLineChartView(QChartView):

    bar_hovered = pyqtSignal(bool, str)

    def __init__(self, data: pd.DataFrame):
        super(VLineChartView, self).__init__()
        self._stocks = data
        self._category = self._stocks['trade_date']
        self._chart = QChart()
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        self._series = QStackedBarSeries()
        # 成交量以万股为单位
        self._vol_multiple = 10000
        self.init_chart()
        self._zero_value = (0, self._chart.axisY().min())
        self._max_value = (len(self._chart.axisX().categories()),
                           self._chart.axisY().max())
        self._zero_point = self._chart.mapToPosition(
            QPointF(self._zero_value[0], self._zero_value[1]))
        self._max_point = self._chart.mapToPosition(
            QPointF(self._max_value[0], self._max_value[1]))
        # 计算x轴单个cate的宽度,用来处理横线不能画到边界
        self._cate_width = (self._max_point.x() - self._zero_point.x()) / len(
            self._category)
        self._series.hovered.connect(self.on_series_hovered)

        x_index_list = np.percentile(range(len(self._category)),
                                     [0, 25, 50, 75, 100])
        self._x_axis_list = [
            QGraphicsSimpleTextItem(self._category[int(index)], self._chart)
            for index in x_index_list
        ]
        [axis.setText(axis.text()[4:]) for axis in self._x_axis_list[1:]]
        self._v_b = QGraphicsSimpleTextItem('B', self._chart)
        self._v_b.setZValue(100)

    def on_series_hovered(self, status, index):
        self.bar_hovered.emit(status, self._category[index])

    def clear_series_value(self):
        self._series.clear()
        self._stocks = None
        self._chart.axisY().setRange(0, 10)
        self._chart.axisX().setCategories(list())

    def add_series_values(self, data: pd.DataFrame, is_init=False):
        self._stocks = data
        bar_red = QBarSet('red')
        bar_red.setColor(Qt.red)
        bar_green = QBarSet('green')
        bar_green.setColor(Qt.green)
        for _, stock in self._stocks.iterrows():
            if stock['open'] < stock['close']:
                bar_red.append(stock['vol'] / self._vol_multiple)
                bar_green.append(0)
            else:
                bar_red.append(0)
                bar_green.append(stock['vol'] / self._vol_multiple)

        self._series.append(bar_red)
        self._series.append(bar_green)

        if not is_init:
            self._stocks = data
            self._category = self._stocks['trade_date']
            axis_x = self._chart.axisX()
            axis_y = self._chart.axisY()
            axis_x.setCategories(self._category)
            max_p = self._stocks[[
                'vol',
            ]].stack().max()
            min_p = self._stocks[[
                'vol',
            ]].stack().min()
            axis_y.setRange(min_p / self._vol_multiple * 0.9,
                            max_p / self._vol_multiple * 1.1)
            self._zero_value = (0, self._chart.axisY().min())
            self._max_value = (len(self._chart.axisX().categories()),
                               self._chart.axisY().max())
            # 计算x轴单个cate的宽度,用来处理横线不能画到边界
            self._cate_width = (self._max_point.x() -
                                self._zero_point.x()) / len(self._category)

    def resizeEvent(self, event):
        super(VLineChartView, self).resizeEvent(event)
        self._zero_point = self._chart.mapToPosition(
            QPointF(self._zero_value[0], self._zero_value[1]))
        self._max_point = self._chart.mapToPosition(
            QPointF(self._max_value[0], self._max_value[1]))
        self._cate_width = (self._max_point.x() - self._zero_point.x()) / len(
            self._category)
        # 绘制自定义X轴
        self._x_axis_list[0].setPos(self._zero_point.x() - self._cate_width,
                                    self._zero_point.y() + 10)
        self._x_axis_list[1].setPos(self._max_point.x() * 0.25,
                                    self._zero_point.y() + 10)
        self._x_axis_list[2].setPos(self._max_point.x() * 0.5,
                                    self._zero_point.y() + 10)
        self._x_axis_list[3].setPos(self._max_point.x() * 0.75,
                                    self._zero_point.y() + 10)
        self._x_axis_list[4].setPos(
            self._max_point.x() - self._x_axis_list[-1].boundingRect().width(),
            self._zero_point.y() + 10)
        # 20180207 这个日期的柱形图上面画一个字母b
        vol = self._stocks[self._stocks['trade_date'] ==
                           '20180207']['vol'] / self._vol_multiple
        print('vol:', vol, ' trade_date:', '20180207')
        pos = self._chart.mapToPosition(
            QPointF(list(self._category).index('20180207'), vol))
        pos = QPointF(pos.x() - self._cate_width / 2,
                      pos.y() - self._v_b.boundingRect().height())
        self._v_b.setPos(pos)

    def max_point(self):
        return QPointF(self._max_point.x() + self._cate_width / 2,
                       self._max_point.y())

    def min_point(self):
        return QPointF(self._zero_point.x() - self._cate_width / 2,
                       self._zero_point.y())

    def init_chart(self):
        self.add_series_values(self._stocks, True)
        self._chart.addSeries(self._series)
        self._chart.createDefaultAxes()
        self._chart.setLocalizeNumbers(True)
        axis_x = self._chart.axisX()
        axis_y = self._chart.axisY()
        axis_x.setGridLineVisible(False)
        axis_y.setGridLineVisible(False)
        axis_y.setLabelFormat("%.2f")
        axis_x.setCategories(self._category)
        axis_x.setLabelsVisible(False)
        max_p = self._stocks[[
            'vol',
        ]].stack().max()
        min_p = self._stocks[[
            'vol',
        ]].stack().min()
        axis_y.setRange(min_p / self._vol_multiple * 0.9,
                        max_p / self._vol_multiple * 1.1)

        # chart的图例
        legend = self._chart.legend()
        legend.hide()
        # 设置图例由Series来决定样式
        # legend.setMarkerShape(QLegend.MarkerShapeFromSeries)

        self.setChart(self._chart)
        self._chart.layout().setContentsMargins(0, 0, 0, 0)
        # 设置内边界的bottom为0
        # margins = self._chart.margins()
        # self._chart.setMargins(QMargins(margins.left(), 0, margins.right(), 0))
        self._chart.setBackgroundRoundness(0)
示例#13
0
class KLineChartView(QChartView):
    def __init__(self):
        super(KLineChartView, self).__init__()
        self.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        self._chart = QChart(title='蜡烛图悬浮提示')
        self.stocks = read_tick_data()
        self.category = [
            trade_date[4:] for trade_date in self.stocks['trade_date']
        ]
        self._count = len(self.category)
        self.resize(800, 300)

        self.init_chart()

        self.toolTipWidget = GraphicsProxyWidget(self._chart)

        # 鼠标跟踪的十字线
        self.lineItem_h = QGraphicsLineItem(self._chart)
        self.lineItem_v = QGraphicsLineItem(self._chart)
        pen = QPen()
        pen.setStyle(Qt.DotLine)
        pen.setColor(QColor(Qt.gray))
        pen.setWidth(2)
        self.lineItem_h.setPen(pen)
        self.lineItem_v.setPen(pen)
        self.lineItem_h.setZValue(100)
        self.lineItem_v.setZValue(100)
        self.lineItem_h.hide()
        self.lineItem_v.hide()

        # 坐标轴上最大最小的值
        # x 轴是
        self.min_x, self.max_x = 0, len(self._chart.axisX().categories())
        self.min_y, self.max_y = self._chart.axisY().min(), self._chart.axisY(
        ).max()
        # y 轴最高点坐标
        self.point_y_max = self._chart.mapToPosition(
            QPointF(self.min_x, self.max_y))
        # x 轴最高点坐标
        self.point_x_max = self._chart.mapToPosition(
            QPointF(self.max_x, self.min_y))
        # self.point_x_min = self._chart.mapToPosition(QPointF(self.min_x, self.min_y))

        # 计算x轴单个cate的宽度,用来处理横线不能画到边界
        self.x_width = (self.point_x_max.x() - self.point_y_max.x()) / len(
            self.category)
        self.x_x_min = self.point_y_max.x() - self.x_width / 2
        self.x_x_max = self.point_x_max.x() - self.x_width / 2

        # 中间位置,用来判断TipWidget放在哪里
        mid_date = self.stocks['trade_date'].iloc[len(
            self.stocks['trade_date']) // 2]
        self.mid_x = float(
            time.mktime(
                datetime.datetime.strptime(str(mid_date),
                                           '%Y%m%d').timetuple()))
        self.left_pos = self.point_y_max
        self.right_pos = self._chart.mapToPosition(
            QPointF(self.max_x, self.max_y))

    def mouseMoveEvent(self, event):
        super(KLineChartView, self).mouseMoveEvent(event)
        pos = event.pos()
        if self.x_x_min < pos.x() < self.x_x_max \
                and self.point_x_max.y() > pos.y() > self.point_y_max.y():
            self.lineItem_h.setLine(self.x_x_min, pos.y(), self.x_x_max,
                                    pos.y())
            self.lineItem_v.setLine(pos.x(), self.point_y_max.y(), pos.x(),
                                    self.point_x_max.y())
            self.lineItem_h.show()
            self.lineItem_v.show()
        else:
            self.lineItem_h.hide()
            self.lineItem_v.hide()

    def resizeEvent(self, event):
        super(KLineChartView, self).resizeEvent(event)
        # y 轴最高点坐标
        self.point_y_max = self._chart.mapToPosition(
            QPointF(self.min_x, self.max_y))
        # x 轴最高点坐标
        self.point_x_max = self._chart.mapToPosition(
            QPointF(self.max_x, self.min_y))
        # 计算x轴单个cate的宽度,用来处理横线不能画到边界
        self.x_width = (self.point_x_max.x() - self.point_y_max.x()) / len(
            self.category)
        self.x_x_min = self.point_y_max.x() - self.x_width / 2
        self.x_x_max = self.point_x_max.x() - self.x_width / 2
        self.left_pos = self.point_y_max
        self.right_pos = self._chart.mapToPosition(
            QPointF(self.max_x, self.max_y))

    def init_chart(self):
        self._chart.setAnimationOptions(QChart.SeriesAnimations)
        series = QCandlestickSeries()
        series.setIncreasingColor(QColor(Qt.red))
        series.setDecreasingColor(QColor(Qt.green))
        series.setName(self.stocks['name'].iloc[0])
        for _, stock in self.stocks.iterrows():
            time_p = datetime.datetime.strptime(stock['trade_date'], '%Y%m%d')
            time_p = float(time.mktime(time_p.timetuple()))
            _set = QCandlestickSet(float(stock['open']), float(stock['high']),
                                   float(stock['low']), float(stock['close']),
                                   time_p, series)
            _set.hovered.connect(self.handleBarHoverd)  # 鼠标悬停
            series.append(_set)
        self._chart.addSeries(series)

        self._chart.createDefaultAxes()
        self._chart.setLocalizeNumbers(True)
        axis_x = self._chart.axisX()
        axis_y = self._chart.axisY()
        axis_x.setGridLineVisible(False)
        axis_y.setGridLineVisible(False)
        axis_x.setCategories(self.category)
        max_p = self.stocks[['high', 'low']].stack().max() + 10
        min_p = self.stocks[['high', 'low']].stack().min() - 10
        axis_y.setRange(min_p, max_p)

        # chart的图例
        legend = self._chart.legend()
        # 设置图例由Series来决定样式
        legend.setMarkerShape(QLegend.MarkerShapeFromSeries)

        self.setChart(self._chart)
        # 设置外边界全部为0
        self._chart.layout().setContentsMargins(0, 0, 0, 0)
        # 设置内边界都为0
        # self._chart.setMargins(QMargins(0, 0, 0, 0))
        # 设置背景区域无圆角
        self._chart.setBackgroundRoundness(0)

    def handleBarHoverd(self, status):
        """ 改变画笔的风格 """
        bar = self.sender()  # 信号发送者
        pen = bar.pen()
        if not pen:
            return
        pen.setStyle(Qt.DotLine if status else Qt.SolidLine)
        bar.setPen(pen)
        if status:
            # 通过 bar 可以获取横轴坐标(timestamp)和纵轴坐标(high)
            # 然后将坐标值转换为位置,显示 TipWidget 的位置
            right_pos = QPointF(
                self.right_pos.x() - self.toolTipWidget.width() - self.x_width,
                self.right_pos.y())
            pos = self.left_pos if bar.timestamp() > self.mid_x else right_pos
            trade_date = time.strftime('%Y%m%d',
                                       time.localtime(bar.timestamp()))
            self.toolTipWidget.show(str(trade_date), str(bar.open()),
                                    str(bar.close()), str(bar.high()),
                                    str(bar.low()), pos)
        else:
            self.toolTipWidget.hide()
示例#14
0
class Ui(mainwindow.Ui_MainWindow):
    def __init__(self, MainWindow):
        super(Ui, self).setupUi(MainWindow)
        self.MainWindow = MainWindow
        self.socketPacketReceiver = None

        self.timmer_is_on = False
        self.hexParser = StixHexStringParser()
        self.socketPacketReceiver = StixSocketPacketReceiver()
        self.socketPacketServer = StixSocketPacketServer()
        self.dataReader = StixFileReader()

        slots = {
            'info': self.onDataReaderInfo,
            'warning': self.onDataReaderWarning,
            'error': self.onDataReaderError,
            'critical': self.onDataReaderCritical,
            'dataLoaded': self.onDataReady,
            'packetArrival': self.onPacketArrival,
            'progress': self.onProgressUpdated
        }

        self.socketPacketReceiver.connectSignalSlots(slots)
        self.dataReader.connectSignalSlots(slots)
        self.hexParser.connectSignalSlots(slots)
        self.socketPacketServer.connectSignalSlots(slots)


        self.tabWidget.setCurrentIndex(0)
        self.actionExit.triggered.connect(self.close)
        self.actionPlot.setEnabled(False)

        self.actionNext.setIcon(self.style().standardIcon(
            QtWidgets.QStyle.SP_ArrowForward))
        self.actionPrevious.setIcon(self.style().standardIcon(
            QtWidgets.QStyle.SP_ArrowBack))
        self.actionOpen.setIcon(self.style().standardIcon(
            QtWidgets.QStyle.SP_DialogOpenButton))
        self.actionSave.setIcon(self.style().standardIcon(
            QtWidgets.QStyle.SP_DriveFDIcon))

        self.actionSave.triggered.connect(self.save)

        self.actionOpen.triggered.connect(self.getOpenFilename)
        self.filterPattern = None

        self.actionNext.triggered.connect(self.nextPacket)
        self.actionPrevious.triggered.connect(self.previousPacket)
        self.actionAbout.triggered.connect(self.about)

        self.actionPrevious.setEnabled(False)
        self.actionNext.setEnabled(False)
        self.actionSave.setEnabled(False)
        self.actionPlot.setEnabled(False)
        self.actionCopy.triggered.connect(self.onCopyTriggered)

        self.packetTreeWidget.currentItemChanged.connect(self.onPacketSelected)

        self.actionCopy.setEnabled(False)
        self.actionPaste.triggered.connect(self.onPasteTriggered)
        self.actionLog.triggered.connect(self.dockWidget.show)
        self.actionSetIDB.triggered.connect(self.onSetIDBClicked)
        self.plotButton.clicked.connect(
            partial(self.onPlotButtonClicked, None))

        #self.progressBar = QtWidgets.QProgressBar()
        #self.statusbar.addPermanentWidget(self.progressBar)
        self.progressDiag = None
        #self.progressBar.hide()

        self.actionPacketServer.triggered.connect(self.startPacketServer)

        self.exportButton.clicked.connect(self.onExportButtonClicked)
        self.actionPlot.triggered.connect(self.onPlotActionClicked)
        self.actionLoadMongodb.triggered.connect(self.onLoadMongoDBTriggered)
        self.actionConnectTSC.triggered.connect(self.onConnectTSCTriggered)
        self.actionPacketFilter.triggered.connect(self.filter)
        self.actionPlugins.triggered.connect(self.onPluginTriggered)
        self.actionOnlineHelp.triggered.connect(self.onOnlineHelpTriggered)
        self.actionViewBinary.triggered.connect(self.onViewBinaryTriggered)
        self.actionTimestampConvertor.triggered.connect(self.onTimestampConvertorTriggered)
        #self.actionPythonConsole.triggered.connect(self.startPythonConsole)
        self.autoUpdateButton.clicked.connect(
            self.onPlotAutoUpdateButtonClicked)

        self.packetTreeWidget.customContextMenuRequested.connect(
            self.packetTreeContextMenuEvent)

        #self.statusListWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        #self.statusListWidget.customContextMenuRequested.connect(self.statusListContextMenuEvent)

        self.mdb = None

        self.current_row = 0
        self.data = []
        self.x = []
        self.y = []
        self.xlabel = 'x'
        self.ylabel = 'y'

        self.buttons_enabled = False

        self.chart = QChart()
        self.chart.layout().setContentsMargins(0, 0, 0, 0)
        self.chart.setBackgroundRoundness(0)
        self.savePlotButton.clicked.connect(self.savePlot)

        self.chartView = QChartView(self.chart)
        self.gridLayout.addWidget(self.chartView, 1, 0, 1, 15)
        self.selected_services = SELECTED_SERVICES
        self.selected_SPID = []
        self.selected_tmtc = 3

        # IDB location

        self.settings = QtCore.QSettings('FHNW', 'stix_parser')
        self.idb_filename = self.settings.value('idb_filename', [], str)
        if self.idb_filename:
            STIX_IDB.reload(self.idb_filename)
        if not STIX_IDB.is_connected():
            self.showMessage('IDB has not been set!')
        else:
            idb_filename = STIX_IDB.get_idb_filename()
            self.showMessage('IDB loaded from : {} '.format(idb_filename), 1)
            if idb_filename != self.idb_filename:
                self.settings.setValue('idb_filename', idb_filename)
                self.idb_filename = idb_filename

    #def startPythonConsole(self):
    #    console.start({'packets': self.data})
    def close(self):
        self.MainWindow.close()
    def style(self):
        return self.MainWindow.style()

    def startPacketServer(self):
        host = 'localhost'
        port = 9096

        self.socketPacketServer.connect(host, port)
        self.socketPacketServer.setData(self.current_row, self.data)
        self.socketPacketServer.start()

        abspath = os.path.dirname(os.path.abspath(__file__))

        template = (
            "import sys\nsys.path.append('{}')\nimport client_packet_request as req\n"
            "packets=req.request(query_str='len', host='{}',port={}, verbose_level=1)\n"
            "#a query_string can be \n"
            "#  -  a python slice notation, for example, ':' '0:-1', 3:-1\n"
            "#  -  'len',  to get the total number of packets,\n"
            "#  -   index ,  to get a packet of the given index"
            "#set verbose_level to 0, to suppress print output  ").format(
                abspath, host, port)
        cb = QtWidgets.QApplication.clipboard()
        cb.clear(mode=cb.Clipboard)
        cb.setText(template, mode=cb.Clipboard)
        msg = QtWidgets.QMessageBox()
        msg.setIcon(QtWidgets.QMessageBox.Information)
        msg.setText(
            "Packet server started and a template to request packet has been copied to your clipboard!"
        )
        retval = msg.exec_()

    def onPlotAutoUpdateButtonClicked(self):
        if not self.timmer_is_on:
            if not self.data:
                return
            num_packets = len(self.data)
            if num_packets > 200:
                packets = self.data[-200:-1]
            else:
                packets = self.data
            self.timer = QTimer()
            self.timer.timeout.connect(
                partial(self.onPlotButtonClicked, packets))
            self.timer.start(2000)
            self.timmer_is_on = True
            self.autoUpdateButton.setText('Stop Auto Update')
        else:
            if self.timer:
                self.timer.stop()
            self.timmer_is_on = False
            self.autoUpdateButton.setText('Start Auto Update')

    #def statusListContextMenuEvent(self,pos):
    #    menu = QtWidgets.QMenu()
    #    clearLogAction= menu.addAction('Empty log')
    #    clearLogAction.triggered.connect(self.clearLog)
    #def clearLog(self):
    #    self.statusListWidget.clear()

    def packetTreeContextMenuEvent(self, pos):
        menu = QtWidgets.QMenu()
        filterAction = menu.addAction('Filter')
        menu.addSeparator()
        rawDataAction = menu.addAction('Raw binary data')
        menu.addSeparator()
        copyPacketAction = menu.addAction('Copy packet')
        menu.addSeparator()
        deleteAllAction = menu.addAction('Delete all packets')
        self.current_row = self.packetTreeWidget.currentIndex().row()

        rawDataAction.triggered.connect(self.onViewBinaryTriggered)
        filterAction.triggered.connect(self.filter)
        copyPacketAction.triggered.connect(self.onCopyTriggered)
        deleteAllAction.triggered.connect(self.onDeleteAllTriggered)
        action = menu.exec_(self.packetTreeWidget.viewport().mapToGlobal(pos))

    def filter(self):
        text, okPressed = QtWidgets.QInputDialog.getText(
            None, "Packet filtering",
            "Filtering by SPID or description (! to exclude):",
            QtWidgets.QLineEdit.Normal, "")
        if okPressed:
            self.filterPattern = text
            self.addPacketsToView(self.data, True, show_stat=False)

    def onDeleteAllTriggered(self):
        self.data.clear()
        self.current_row = 0
        self.packetTreeWidget.clear()
        self.paramTreeWidget.clear()

    #def onPacketTreeItemDoubleClicked(self):
    #    self.onViewBinaryTriggered()

    def onViewBinaryTriggered(self):
        diag = QtWidgets.QDialog()
        diag_ui = raw_viewer.Ui_Dialog()
        diag_ui.setupUi(diag)
        if self.data:
            try:
                raw = self.data[self.current_row]['bin']
                header = self.data[self.current_row]['header']
                diag_ui.setPacketInfo('{}({},{})  {}'.format(
                    header['TMTC'], header['service_type'],
                    header['service_subtype'], header['descr']))
                diag_ui.displayRaw(raw)
            except (IndexError, KeyError):
                diag_ui.setText('Raw data not available.')
        diag.exec_()

    def onOnlineHelpTriggered(self):
        webbrowser.open(
            'https://github.com/i4Ds/STIX-python-data-parser', new=2)

    def onTimestampConvertorTriggered(self):
        diag = QtWidgets.QDialog()
        diag_ui = timestamp_convertor.Ui_Dialog()
        diag_ui.setupUi(diag)
        diag.exec_()


    def onPluginTriggered(self):
        self.plugin_location = self.settings.value('plugin_location', [], str)
        diag = QtWidgets.QDialog()
        diag_ui = plugin.Ui_Dialog()
        diag_ui.setupUi(diag)
        if self.plugin_location:
            diag_ui.setPluginLocation(self.plugin_location)

        diag_ui.setData(self.data, self.current_row)
        diag.exec_()
        location = diag_ui.getPluginLocation()
        if location != self.plugin_location:
            self.settings.setValue('plugin_location', location)

    def onPacketFilterTriggered(self):
        diag = QtWidgets.QDialog()
        diag_ui = packet_filter.Ui_Dialog()
        diag_ui.setupUi(diag)
        self.filterPattern = ''  #empty search string
        diag_ui.setSelectedServices(self.selected_services)

        diag_ui.buttonBox.accepted.connect(
            partial(self.applyServiceFilter, diag_ui))
        diag.exec_()

    def applyServiceFilter(self, diag_ui):
        self.selected_SPID = diag_ui.getSelectedSPID()
        self.selected_services = diag_ui.getSelectedServices()
        self.selected_tmtc = diag_ui.getTMTC()
        self.showMessage('Applying filter...')

        self.addPacketsToView(self.data, True, show_stat=False)

    def onExportButtonClicked(self):
        if self.y:
            filename = str(
                QtWidgets.QFileDialog.getSaveFileName(
                    None, "Save data to file", "", "CSV(*.csv)")[0])
            if filename:
                with open(filename, 'w') as f:
                    f.write('{},{}\n'.format(self.xlabel, self.ylabel))
                    for xx, yy in zip(self.x, self.y):
                        f.write('{},{}\n'.format(xx, yy))
                    self.showMessage(
                        'The data has been written to {}'.format(filename))
        else:
            msgBox = QtWidgets.QMessageBox()
            msgBox.setIcon(QtWidgets.QMessageBox.Information)
            msgBox.setText('Plot first!')
            msgBox.setWindowTitle("Warning")
            msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
            msgBox.exec_()

    def savePlot(self):
        # if self.figure.get_axes():
        if self.chart:
            filetypes = "PNG (*.png);;JPEG (*.jpg)"
            filename = str(
                QtWidgets.QFileDialog.getSaveFileName(
                    None, "Save plot to file", "", filetypes)[0])
            if filename:
                if not filename.endswith(('.png', '.jpg')):
                    filename += '.png'
                # self.figure.savefig(filename)
                p = self.chartView.grab()
                p.save(filename)
                self.showMessage(('Has been saved to %s.' % filename))

        else:
            msgBox = QtWidgets.QMessageBox()
            msgBox.setIcon(QtWidgets.QMessageBox.Information)
            msgBox.setText('No figure to save')
            msgBox.setWindowTitle("STIX raw data viewer")
            msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
            msgBox.exec_()

    def onCopyTriggered(self):
        packet_id = self.current_row
        try:
            packet = self.data[packet_id]
            ss = pprint.pformat(packet)

            cb = QtWidgets.QApplication.clipboard()
            cb.clear(mode=cb.Clipboard)
            cb.setText(ss, mode=cb.Clipboard)
            msg = QtWidgets.QMessageBox()
            msg.setIcon(QtWidgets.QMessageBox.Information)
            msg.setText(
                "The data of the selected packet has been copied to the clipboard."
            )
            msg.setWindowTitle("Information")
            msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
            retval = msg.exec_()

        except Exception as e:
            self.showMessage(str(e), 0)

    def onPasteTriggered(self):
        raw_hex = QtWidgets.QApplication.clipboard().text()
        if len(raw_hex) < 16:
            self.showMessage('No data in the clipboard.')
            return
        self.hexParser.setHex(raw_hex)
        self.hexParser.start()

    def showMessage(self, msg, where=0):
        if where != 1:
            self.statusbar.showMessage(msg)
        if where != 0:
            self.statusListWidget.addItem(msg)

    def onSetIDBClicked(self):
        self.idb_filename = QtWidgets.QFileDialog.getOpenFileName(
            None, 'Select file', '.', 'IDB file(*.db *.sqlite *.sqlite3)')[0]

        if not self.idb_filename:
            return

        STIX_IDB.reload(self.idb_filename)
        if STIX_IDB.is_connected():
            #settings = QtCore.QSettings('FHNW', 'stix_parser')
            self.settings.setValue('idb_filename', self.idb_filename)
        self.showMessage(
            'IDB location: {} '.format(STIX_IDB.get_idb_filename()), 1)

    def save(self):
        filetypes = 'python compressed pickle (*.pklz);; python pickle file (*.pkl);; binary data (*.dat)'
        self.output_filename = str(
            QtWidgets.QFileDialog.getSaveFileName(None, "Save packets to", "",
                                                  filetypes)[0])

        if not self.output_filename.endswith(('.pklz', '.pkl', '.dat')):
            msg = 'unsupported file format !'
            self.showMessage(msg)
            return

        msg = 'Writing data to file %s' % self.output_filename
        self.showMessage(msg)
        if self.output_filename.endswith(('.pklz', '.pkl')):
            stw = stix_writer.StixPickleWriter(self.output_filename)
            stw.register_run(str(self.input_filename))
            stw.write_all(self.data)
        elif self.output_filename.endswith('.dat'):
            stw = stix_writer.StixBinaryWriter(self.output_filename)
            stw.write_all(self.data)
            num_ok = stw.get_num_sucess()
            msg = (
                'The binary data of {} packets written to file {}, total packets {}'
                .format(num_ok, self.output_filename, len(self.data)))
            self.showMessage(msg)
        msg = 'Packets have been written to %s' % self.output_filename
        self.showMessage(msg)

    def setListViewSelected(self, row):
        #index = self.model.createIndex(row, 0);
        # if index.isValid():
        #    self.model.selectionModel().select( index, QtGui.QItemSelectionModel.Select)
        pass

    def about(self):
        msgBox = QtWidgets.QMessageBox()
        msgBox.setIcon(QtWidgets.QMessageBox.Information)
        msgBox.setText("STIX raw data parser and viewer, [email protected]")
        msgBox.setWindowTitle("Stix data viewer")

        msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
        msgBox.exec_()

    def nextPacket(self):
        self.current_row += 1
        length = len(self.data)

        if self.current_row >= length:
            self.current_row = length - 1
            self.showMessage('No more packet!')

        self.showPacket(self.current_row)
        self.setListViewSelected(self.current_row)

    def previousPacket(self):
        self.current_row -= 1
        if self.current_row < 0:
            self.current_row = 0
            self.showMessage('Reach the first packet!')

        self.showPacket(self.current_row)
        self.setListViewSelected(self.current_row)

    def getOpenFilename(self):

        location = self.settings.value('location', [], str) 
        if not location:
            location = '.'


        filetypes = (
            'Supported file (*.dat *.bin *.binary *.pkl *.pklz *.xml *ascii *BDF *txt) ;; All(*)'
        )
        self.input_filename = QtWidgets.QFileDialog.getOpenFileName(
            None, 'Select file', location, filetypes)[0]
        if not self.input_filename:
            return
        self.settings.setValue('location', os.path.abspath(self.input_filename))

        diag = QtWidgets.QDialog()
        diag_ui = packet_filter.Ui_Dialog()
        diag_ui.setupUi(diag)
        diag_ui.setSelectedServices(SELECTED_SERVICES)
        diag_ui.buttonBox.accepted.connect(
            partial(self.onOpenFile, self.input_filename, diag_ui))
        diag.exec_()

    def onOpenFile(self, input_filename, diag):
        self.selected_SPID = diag.getSelectedSPID()
        self.selected_services = diag.getSelectedServices()
        self.openFile(input_filename, self.selected_services,
                      self.selected_SPID)

    def openFile(self, filename, selected_services=None, selected_SPID=None):
        msg = 'Loading file %s ...' % filename
        self.progressDiag = QtWidgets.QProgressDialog()
        #self.showMessage(msg)
        self.progressDiag.setLabelText(msg)
        self.progressDiag.setWindowTitle('Loading data')
        self.progressDiag.setCancelButtonText('Cancel')
        self.progressDiag.setRange(0, 100)
        self.progressDiag.setMinimumWidth(300)
        self.progressDiag.canceled.connect(self.stopParsing)
        self.filterPattern = ''
        self.dataReader.setPacketFilter(selected_services, selected_SPID)
        self.dataReader.setFilename(filename)
        self.dataReader.start()
        self.progressDiag.show()

    def stopParsing(self):
        if self.dataReader:
            self.dataReader.stopParsing()
            self.progressDiag.hide()

    def onProgressUpdated(self, progress):
        if not self.progressDiag:
            return
        self.progressDiag.setValue(progress)
        if progress >=99:
            self.progressDiag.hide()

    def onDataReaderCritical(self, msg):
        self.showMessage(msg, 1)

    def onDataReaderInfo(self, msg):
        self.showMessage(msg, 0)

    def onDataReaderWarning(self, msg):
        self.showMessage(msg, 1)

    def onDataReaderError(self, msg):
        self.showMessage(msg, 1)

    def onDataReady(self, data, clear=True, show_stat=True):
        #self.progressBar.hide()
        if not clear:
            self.data.extend(data)
        else:
            self.data = data
        if data:
            self.addPacketsToView(data, clear=clear, show_stat=show_stat)
            self.enableButtons()
        else:
            self.showMessage('No packet loaded')

    def enableButtons(self):
        if not self.buttons_enabled:
            self.actionPrevious.setEnabled(True)
            self.actionNext.setEnabled(True)
            self.actionSave.setEnabled(True)
            self.actionCopy.setEnabled(True)
            self.actionPlot.setEnabled(True)
            self.actionViewBinary.setEnabled(True)
            self.buttons_enabled = True

    def addPacketsToView(self, data, clear=True, show_stat=True):
        if clear:
            self.packetTreeWidget.clear()

        for p in data:
            if not isinstance(p, dict):
                continue
            header = p['header']
            root = QtWidgets.QTreeWidgetItem(self.packetTreeWidget)
            colors = {2: '#FFA500', 1: '#000080', 3: '#FF0000', 4: '#800000'}
            tc_color = '#78281F'
            if header['TMTC'] == 'TC':
                root.setForeground(0, QtGui.QBrush(QtGui.QColor(tc_color)))
                root.setForeground(1, QtGui.QBrush(QtGui.QColor(tc_color)))
            else:
                if header['service_type'] == 5:
                    if header['service_subtype'] in colors.keys():
                        root.setForeground(
                            0,
                            QtGui.QBrush(
                                QtGui.QColor(
                                    colors[header['service_subtype']])))
                        root.setForeground(
                            1,
                            QtGui.QBrush(
                                QtGui.QColor(
                                    colors[header['service_subtype']])))

            timestamp_str = stix_datetime.format_datetime(header['unix_time'])
            root.setText(0, timestamp_str)
            description = '{}({},{}) - {}'.format(
                header['TMTC'], header['service_type'],
                header['service_subtype'], header['descr'])
            root.setText(1, description)
            hidden = False

            if self.selected_SPID:
                if header['TMTC'] == 'TC':
                    hidden = True
                elif -int(header['SPID']) in self.selected_SPID or int(
                        header['SPID']) not in self.selected_SPID:
                    hidden = True
            else:
                if int(header['service_type']) not in self.selected_services:
                    hidden = True

            TMTC = header['TMTC']
            if TMTC == 'TM' and self.selected_tmtc in [2, 0]:
                hidden = True
            if TMTC == 'TC' and self.selected_tmtc in [1, 0]:
                hidden = True


            if self.filterPattern:
                to_exclude = False
                pattern = self.filterPattern.strip()
                if pattern.startswith('!'):
                    to_exclude = True
                    pattern = pattern[1:]
                try:
                    spid = int(pattern)
                    hidden = to_exclude == (header['SPID'] == spid)
                    #XNOR operation
                except (TypeError, ValueError):
                    hidden = to_exclude == (pattern in description)

            root.setHidden(hidden)

        if show_stat:
            total_packets = len(self.data)
            self.showMessage(('Total packet(s): %d' % total_packets))

    def onConnectTSCTriggered(self):
        diag = QtWidgets.QDialog()
        diag_ui = tsc_connection.Ui_Dialog()
        diag_ui.setupUi(diag)
        self.tsc_host = self.settings.value('tsc_host', [], str)
        self.tsc_port = self.settings.value('tsc_port', [], str)
        if self.tsc_host:
            diag_ui.serverLineEdit.setText(self.tsc_host)
        if self.tsc_port:
            diag_ui.portLineEdit.setText(self.tsc_port)
        diag_ui.buttonBox.accepted.connect(partial(self.connectToTSC, diag_ui))
        diag.exec_()

    def connectToTSC(self, dui):
        host = dui.serverLineEdit.text()
        port = dui.portLineEdit.text()
        self.showMessage('Connecting to TSC...')

        self.socketPacketReceiver.connect(host, int(port))
        self.socketPacketReceiver.start()

    def onPacketArrival(self, packets):
        clear = False
        if packets:
            if len(self.data) > MAX_NUM_PACKET_IN_BUFFER:
                clear = True
            self.onDataReady(packets, clear=clear, show_stat=True)

    def onLoadMongoDBTriggered(self):
        diag = QtWidgets.QDialog()
        diag_ui = mongo_dialog.Ui_Dialog()
        diag_ui.setupUi(diag)
        #self.settings = QtCore.QSettings('FHNW', 'stix_parser')
        self.mongo_server = self.settings.value('mongo_server', [], str)
        self.mongo_port = self.settings.value('mongo_port', [], str)
        self.mongo_user = self.settings.value('mongo_user', [], str)
        self.mongo_pwd = self.settings.value('mongo_pwd', [], str)

        if self.mongo_server:
            diag_ui.serverLineEdit.setText(self.mongo_server)
        if self.mongo_port:
            diag_ui.portLineEdit.setText(self.mongo_port)

        if self.mongo_user:
            diag_ui.userLineEdit.setText(self.mongo_user)
        if self.mongo_pwd:
            diag_ui.pwdLineEdit.setText(self.mongo_pwd)

        diag_ui.pushButton.clicked.connect(
            partial(self.loadRunsFromMongoDB, diag_ui))
        diag_ui.buttonBox.accepted.connect(
            partial(self.loadDataFromMongoDB, diag_ui, diag))
        diag.exec_()

    def loadRunsFromMongoDB(self, dui):
        server = dui.serverLineEdit.text()
        port = dui.portLineEdit.text()
        user = dui.userLineEdit.text()
        pwd = dui.pwdLineEdit.text()

        self.showMessage('saving setting...')
        if self.mongo_server != server:
            self.settings.setValue('mongo_server', server)
        if self.mongo_port != port:
            self.settings.setValue('mongo_port', port)

        if self.mongo_user != user:
            self.settings.setValue('mongo_user', user)
        if self.mongo_pwd != pwd:
            self.settings.setValue('mongo_pwd', pwd)

        self.showMessage('connecting Mongo database ...')
        self.mdb = mgdb.MongoDB(server, int(port), user, pwd)
        if not self.mdb.is_connected():
            self.showMessage('Failed to connect to MongoDB')
            return

        dui.treeWidget.clear()
        self.showMessage('Fetching data...')
        for run in self.mdb.select_all_runs():
            root = QtWidgets.QTreeWidgetItem(dui.treeWidget)
            root.setText(0, str(run['_id']))
            root.setText(1, run['filename'])
            root.setText(2, stix_datetime.format_datetime(run['date']))
            root.setText(3, stix_datetime.format_datetime(run['data_start_unix_time']))
            root.setText(4, stix_datetime.format_datetime(run['data_stop_unix_time']))

    def loadDataFromMongoDB(self, dui, diag):
        self.showMessage('Loading packets ...')
        selected_runs = []
        for item in dui.treeWidget.selectedItems():
            selected_runs.append(item.text(0))
        if not selected_runs:
            self.showMessage('Run not selected!')
        if selected_runs:
            diag.done(0)
            self.showMessage('Loading data ...!')
            data = list(self.mdb.select_packets_by_run(selected_runs[0]))
            if data:
                self.onDataReady(data, clear=True)
            else:
                self.showMessage('No packets found!')
        # close

    def onPacketSelected(self, cur, pre):
        self.current_row = self.packetTreeWidget.currentIndex().row()
        self.showMessage(('Packet #%d selected' % self.current_row))
        self.showPacket(self.current_row)

    def showPacket(self, row):
        if not self.data:
            return
        header = self.data[row]['header']
        total_packets = len(self.data)
        self.showMessage(
            ('Packet %d / %d  %s ' % (row, total_packets, header['descr'])))
        self.paramTreeWidget.clear()
        header_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget)
        header_root.setText(0, "Header")
        rows = len(header)
        for key, val in header.items():
            root = QtWidgets.QTreeWidgetItem(header_root)
            root.setText(0, key)
            root.setText(1, str(val))

        params = self.data[row]['parameters']
        param_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget)
        param_root.setText(0, "Parameters")
        self.showParameterTree(params, param_root)
        self.paramTreeWidget.expandItem(param_root)
        self.paramTreeWidget.expandItem(header_root)
        self.current_row = row

    def showParameterTree(self, params, parent, parent_id=[]):
        if not params:
            return
        for i, p in enumerate(params):
            root = QtWidgets.QTreeWidgetItem(parent)
            if not p:
                continue
            param = Parameter(p)
            param_name = param['name']
            desc = param['desc']
            current_ids = parent_id[:]
            current_ids.append(i)
            root.setText(0, param_name)

            root.setText(1, desc)
            root.setText(2, str(param['raw']))
            tip='parameter'+''.join(['[{}]'.format(x) for x in current_ids])
            root.setToolTip(0, tip)

            long_desc = STIX_IDB.get_scos_description(param_name)
            if long_desc:
                root.setToolTip(1, long_desc)

            try:
                root.setToolTip(2, hex(param['raw_int']))
            except:
                pass
            unit=STIX_IDB.get_parameter_unit(param_name)
            eng=str(param['eng'])
            root.setText(3, eng)
            root.setText(4, unit)
            if 'NIXG' in param_name:
                root.setHidden(True)
                #groups should not be shown
            if param.children:
                self.showParameterTree(param['children'], root, current_ids)
        self.paramTreeWidget.itemDoubleClicked.connect(self.onTreeItemClicked)

    def walk(self, name, params, header, ret_x, ret_y, xaxis=0, data_type=0):
        if not params:
            return
        timestamp = header['unix_time']
        for p in params:
            if not p:
                continue
            param = Parameter(p)
            if name == param.name:
                values = None
                if data_type == 0:
                    values = param.raw
                else:
                    values = param.eng
                try:
                    yvalue = float(values)
                    ret_y.append(yvalue)
                    if xaxis == 1:
                        ret_x.append(timestamp)
                    else:
                        self.showMessage(('Can not plot %s  ' % str(yvalue)))
                except Exception as e:
                    self.showMessage(('%s ' % str(e)))

            if param.children:
                self.walk(name, param.children, header, ret_x, ret_y, xaxis,
                          data_type)

    def onPlotButtonClicked(self, packets=None):
        if self.chart:
            self.chart.removeAllSeries()
        if packets is None:
            packets = self.data
        if not packets:
            return

        self.showMessage('Preparing plot ...')
        name = self.paramNameEdit.text()
        packet_selection = self.comboBox.currentIndex()
        xaxis_type = self.xaxisComboBox.currentIndex()
        data_type = self.dataTypeComboBox.currentIndex()
        timestamp = []
        self.y = []
        params = self.paramNameEdit.text()
        header = packets[0]['header']
        current_spid = 0
        spid_text = self.spidLineEdit.text()
        if spid_text:
            current_spid = int(spid_text)
        selected_packets=[] 
        if packet_selection == 0:
            selected_packets=[packets[self.current_row]]
        elif packet_selection == 1:
            selected_packets=packets

        for packet in selected_packets:
            header = packet['header']
            if packet['header']['SPID'] != current_spid:
                continue
            params = packet['parameters']
            self.walk(name, params, header, timestamp, self.y, xaxis_type,
                      data_type)


        self.x = []

        if not self.y:
            self.showMessage('No data points')
        elif self.y:
            style = self.styleEdit.text()
            if not style:
                style = '-'
            title = '%s' % str(name)
            desc = self.descLabel.text()
            if desc:
                title += '- %s' % desc

            self.chart.setTitle(title)

            ylabel = 'Raw value'
            xlabel = name
            if data_type == 1:
                ylabel = 'Engineering / Decompressed  value'
            if xaxis_type == 0:
                if packet_selection == 1:
                    xlabel = "Packet #"
                else:
                    xlabel = "Repeat #"
                self.x = range(0, len(self.y))
            if xaxis_type == 1:
                self.x = [t - timestamp[0] for t in timestamp]
                xlabel = 'Time -T0 (s)'

            if xaxis_type != 2:
                series = QLineSeries()
                series2 = None
                for xx, yy in zip(self.x, self.y):
                    series.append(xx, yy)
                if 'o' in style:
                    series2 = QScatterSeries()
                    for xx, yy in zip(self.x, self.y):
                        series2.append(xx, yy)
                    self.chart.addSeries(series2)
                self.chart.addSeries(series)
                axisX = QValueAxis()
                axisX.setTitleText(xlabel)
                axisY = QValueAxis()
                axisY.setTitleText(ylabel)

                self.chart.setAxisX(axisX)
                self.chart.setAxisY(axisY)
                series.attachAxis(axisX)
                series.attachAxis(axisY)
            else:
                nbins = len(set(self.y))
                ycounts, xedges = np.histogram(self.y, bins=nbins)
                series = QLineSeries()
                for i in range(0, nbins):
                    meanx = (xedges[i] + xedges[i + 1]) / 2.
                    series.append(meanx, ycounts[i])
                self.chart.addSeries(series)
                axisX = QValueAxis()
                axisX.setTitleText(name)
                axisY = QValueAxis()
                axisY.setTitleText("Counts")

                self.chart.setAxisY(axisY)
                self.chart.setAxisX(axisX)
                series.attachAxis(axisX)
                series.attachAxis(axisY)

            self.xlabel = xlabel
            self.ylabel = ylabel
            self.chartView.setRubberBand(QChartView.RectangleRubberBand)
            self.chartView.setRenderHint(QtGui.QPainter.Antialiasing)
            msg = 'Number of data points: {}, Ymin: {}, Ymax: {}'.format(
                len(self.y), min(self.y), max(self.y))
            self.showMessage(msg, 1)

            self.showMessage('The canvas updated!')

    def plotParameter(self, SPID=None, pname=None, desc=None):
        self.tabWidget.setCurrentIndex(1)
        if pname:
            self.paramNameEdit.setText(pname)
        if desc:
            self.descLabel.setText(desc)
        if SPID:
            self.spidLineEdit.setText(str(SPID))

    def onPlotActionClicked(self):
        self.tabWidget.setCurrentIndex(1)
        self.plotParameter()

    def onTreeItemClicked(self, it, col):
        SPID = None
        try:
            SPID = self.data[self.current_row]['header']['SPID']
        except IndexError:
            pass
        self.plotParameter(SPID, it.text(0), it.text(1))
class SENSOR_TEST(QWidget):
    
    bars = None
    series = None
    chart = None
    labelWidgets = []
    comms = None
    commsStatus = False
    deviceID = "HARP"

    def __init__(self):
        QWidget.__init__(self)

        # ADD GUI WIDGETS
        self.setupLayout()

        # LAUNCH GUI
        self.show()
        self.resize(800,600)

    def setupLayout(self):
        parentLayout = QVBoxLayout()
        parentLayout.setContentsMargins(20,20,20,20)

        buttonLayout = QHBoxLayout()

        # CREATE SERIAL COMMUNICATION CONNECT BUTTON
        self.connectButton = QPushButton("CONNECT")
        self.connectButton.setFixedSize(150, 40)
        self.connectButton.setCheckable(True)
        self.connectButton.setStyleSheet("QPushButton {background-color: #66BB6A; color: black; border-radius: 20px}" 
                                    "QPushButton:hover {background-color: #4CAF50; color: black; border-radius: 20px}"
                                    "QPushButton:checked {background-color: #EF5350; color: white; border-radius: 20px}"
                                    "QPushButton:checked:hover {background-color: #F44336; color: white; border-radius: 20px}")

        # CREATE SENSORS CALIBRATION BUTTON
        self.calibrateButton = QPushButton("CALIBRATE")
        self.calibrateButton.setFixedSize(150, 40)
        self.calibrateButton.setStyleSheet("QPushButton {background-color: #E0E0E0; color: black; border-radius: 20px}" 
                                    "QPushButton:hover {background-color: #BDBDBD; color: black; border-radius: 20px}" 
                                    "QPushButton:pressed {background-color: #D5D5D5; color: black; border-radius: 20px}")

        # ADD BUTTONS TO HORIZONTAL LAYOUT
        buttonLayout.addWidget(self.connectButton, alignment = Qt.AlignLeft)
        buttonLayout.addWidget(self.calibrateButton, alignment = Qt.AlignRight)
        
        # CREATE BAR CHART TO DISPLAY SENSOR READINGS
        self.bars = QBarSet('Sensor Readings')
        self.bars.append([0 for i in range(5)])
        self.chart = QChart()
        self.chart.setBackgroundRoundness(20)
        self.chart.layout().setContentsMargins(0, 0, 0, 0)
        self.series = QBarSeries()
        self.series.append(self.bars)
        self.chart.addSeries(self.series)
        self.chart.setTitle('Sensor Readings')

        # CREATE X-AXIS AS SENSOR LABELS
        xAxis = QBarCategoryAxis()
        labels = ["Sensor {}".format(i+1) for i in range(5)]
        xAxis.append(labels)
        
        # CREATE Y-AXIS AND SENSOR READINGS
        yAxis = QValueAxis()
        yAxis.setRange(-100, 100)
        yAxis.setTickCount(11)
        yAxis.setTitleText("Pressure (%)")
        
        # ADD AXES TO CHART
        self.chart.addAxis(xAxis, Qt.AlignBottom)
        self.chart.addAxis(yAxis, Qt.AlignLeft)
        self.chart.legend().setVisible(False)

        # ATTACH AXES TO SERIES
        self.series.attachAxis(xAxis)
        self.series.attachAxis(yAxis)
        
        # ADD CHART TO A VIEW
        chartView = QChartView(self.chart)

        labelLayout = QFrame()
        labelLayout.setStyleSheet("background-color: white; border-radius: 20px")
        layout1 = QVBoxLayout()
        labelLayout.setLayout(layout1)
        layout2 = QHBoxLayout()
        layout3 = QHBoxLayout()
        
        for i in range(5):
            label = QLabel("Sensor {}".format(i+1))
            label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
            label.setStyleSheet("font-weight: bold;")
            layout2.addWidget(label)

            value = QLabel("0 mV")
            value.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
            self.labelWidgets.append(value)
            layout3.addWidget(value)

        layout1.addLayout(layout2, 1)
        layout1.addLayout(layout3, 1)

        parentLayout.addLayout(buttonLayout)
        parentLayout.addWidget(chartView, 5)
        parentLayout.addWidget(labelLayout, 1)

        # LINK WIDGETS
        self.connectButton.clicked.connect(self.serialToggle)
        self.calibrateButton.clicked.connect(self.sensorCalibrate)

        self.setLayout(parentLayout)

    def getSensorReadings(self):
        """
        PURPOSE

        Requests sensor readings from ROV and updates GUI.

        INPUT

        NONE

        RETURNS

        NONE
        """
        # SENSOR POLLING RATE (HZ)
        refreshRate = 100

        # START QTIMER TO REPEATEDLY UPDATE SENSORS AT THE DESIRED POLLING RATE 
        self.timer = QTimer()
        self.timer.setTimerType(Qt.PreciseTimer)
        self.timer.timeout.connect(self.getSensorReadings)
        self.timer.start(int(1000*1/refreshRate))
        
        # STOP REQUESTING SENSOR VALUES IF ROV IS DISCONNECTED
        if self.commsStatus == False:
            self.timer.stop()
        
        else:
            # REQEST SINGLE READING
            sensorReadings = self.getSensors()

            # UPDATE VOLTAGE READINGS
            self.updateVoltageReadings(sensorReadings)

            # SCALE SENSOR READINGS
            scaledReadings = self.mapPressureReadings(sensorReadings)
            
            try:
                # UPDATE GUI 
                self.series.remove(self.bars)
                self.bars = QBarSet("")
                self.bars.append([float(item) for item in scaledReadings])
                self.series.append(self.bars)
            except:
                self.teensyDisconnect()

    def mapPressureReadings(self, values):
        mappedValues = []

        for value in values:
            try:
                mappedValues.append((200/1023)*float(value) - 100)
            except:
                pass

        return mappedValues

    def updateVoltageReadings(self, values):
        voltageValues = [round((3300/1023)*float(i)) for i in values]
        for i, label in enumerate(self.labelWidgets):
            label.setText(str(voltageValues[i]) + " mV")

    def serialToggle(self, buttonState):
        """
        PURPOSE

        Determines whether to connect or disconnect from the ROV serial interface.

        INPUT

        - buttonState = the state of the button (checked or unchecked).

        RETURNS

        NONE
        """
        # CONNECT
        if buttonState:
            self.teensyConnect()

        # DISCONNECT
        else:
            self.teensyDisconnect()

    def sensorCalibrate(self):
        """
        PURPOSE

        INPUT

        RETURNS

        """
        self.calibrateSensors()

    ########################
    ### SERIAL FUNCTIONS ###
    ########################
    def teensyConnect(self):
        """
        PURPOSE

        Attempts to connect to the ROV using the comms library.
        Changes the appearance of the connect buttons.
        If connection is successful, the ROV startup procedure is initiated.

        INPUT

        NONE

        RETURNS

        NONE
        """
        # DISABLE BUTTONS TO AVOID DOUBLE CLICKS
        self.connectButton.setEnabled(False)
            
        # FIND ALL AVAILABLE COM PORTS
        availableComPorts, comPort, identity = self.findComPorts(115200, self.deviceID)
        print(availableComPorts, comPort, identity)
        
        # ATTEMPT CONNECTION TO ROV COM PORT
        status, message = self.serialConnect(comPort, 115200)
        print(status, message)

        # IF CONNECTION IS SUCCESSFUL
        if status == True:
            # MODIFY BUTTON STYLE
            self.connectButton.setText('DISCONNECT')

            # START READING SENSOR VALUES
            self.getSensorReadings()
        
        # IF CONNECTION IS UNSUCCESSFUL
        else:
            self.teensyDisconnect()
            
        # RE-ENABLE CONNECT BUTTONS
        self.connectButton.setEnabled(True)

    def teensyDisconnect(self):
        """
        PURPOSE

        Disconnects from the ROV using the comms library.
        Changes the appearance of the connect buttons

        INPUT

        NONE

        RETURNS

        NONE
        """
        # MODIFY BUTTON STYLE
        self.connectButton.setText('CONNECT')
        self.connectButton.setChecked(False)
        
        # CLOSE COM PORT
        if self.commsStatus:
            self.comms.close()
            self.commsStatus = False

    def findComPorts(self, baudRate, identity):
        """
        PURPOSE

        Find all available COM ports and requests the devices identity.

        INPUT

        - menuObject = pointer to the drop down menu to display the available COM ports.
        - baudRate = baud rate of the serial interface.
        - identity = string containing the required device identity to connect to.

        RETURNS

        - availableComPorts = list of all the available COM ports.
        - rovComPort = the COM port that belongs to the device.
        - identity = the devices response from an identity request.
        """
        # CREATE LIST OF ALL POSSIBLE COM PORTS
        ports = ['COM%s' % (i + 1) for i in range(256)] 

        deviceIdentity = ""
        comPort = None
        availableComPorts = []
        
        # CHECK WHICH COM PORTS ARE AVAILABLE
        for port in ports:
            try:
                comms = serial.Serial(port, baudRate, timeout = 1)
                availableComPorts.append(port)

                # REQUEST IDENTITY FROM COM PORT
                self.commsStatus = True
                deviceIdentity = self.getIdentity(comms, identity)
                comms.close()
                self.commsStatus = False
                
                # FIND WHICH COM PORT IS THE ROV
                if deviceIdentity == identity:
                    comPort = port
                    break
                    
            # SKIP COM PORT IF UNAVAILABLE
            except (OSError, serial.SerialException):
                pass

        return availableComPorts, comPort, deviceIdentity

    def getIdentity(self, serialInterface, identity):
        """
        PURPOSE

        Request identity from a defined COM port.

        INPUT

        - serialInterface = pointer to the serial interface object.
        - identity = the desired identity response from the device connected to the COM port.

        RETURNS

        - identity = the devices response.
        """
        identity = ""
        startTime = datetime.now()
        elapsedTime = 0
        # REPEATIDELY REQUEST IDENTIFICATION FROM DEVICE FOR UP TO 3 SECONDS
        while (identity == "") and (elapsedTime < 3):
            self.serialSend("?I", serialInterface)
            identity = self.serialReceive(serialInterface)
            elapsedTime = (datetime.now() - startTime).total_seconds()

        return identity

    def serialConnect(self, comPort, baudRate):
        """
        PURPOSE

        Attempts to initialise a serial communication interface with a desired COM port.

        INPUT

        - rovComPort = the COM port of the ROV.
        - baudRate = the baud rate of the serial interface.

        RETURNS

        NONE
        """
        self.commsStatus = False
        if comPort != None:
            try: 
                self.comms = serial.Serial(comPort, baudRate, timeout = 1)
                message = "Connection to ROV successful."
                self.commsStatus = True
            except:
                message = "Failed to connect to {}.".format(comPort)
        else:
            message = "Failed to recognise device identity."

        return self.commsStatus, message

    def serialSend(self, command, serialInterface):
        """
        PURPOSE

        Sends a string down the serial interface to the ROV.

        INPUT

        - command = the command to send.
        - serialInterface = pointer to the serial interface object.

        RETURNS

        NONE
        """
        if self.commsStatus:
            try:
                serialInterface.write((command + '\n').encode('ascii'))
            except:
                self.teensyDisconnect()
                print("Failed to send command.")

    def serialReceive(self, serialInterface):
        """
        PURPOSE

        Waits for data until a newline character is received.

        INPUT

        - serialInterface = pointer to the serial interface object.

        RETURNS

        NONE
        """
        received = ""
        try:
            received = serialInterface.readline().decode('ascii').strip()
        except:
            self.teensyDisconnect()
            print("Failed to receive data.")
            
        return(received)

    def getSensors(self):
        """
        PURPOSE

        Send request to device to return sensor readings.
        
        INPUT

        NONE

        RETURNS

        - results = array containing the sensor readings.
        """
        results = []

        # REQUEST SENSOR READINGS
        command = "?S"
        self.serialSend(command, self.comms)
        
        # READ RESPONSE INTO AN ARRAY
        results = self.serialReceive(self.comms).split(",")
        print(results)
        return results

    def calibrateSensors(self):
        """
        """
        # REQUEST SENSOR READINGS
        command = "?C"
        self.serialSend(command, self.comms)
示例#16
0
    def agg_results(self) -> None:
        """
        Display results in the corresponding chart view.

        :return: nothing
        :rtype: None
        """

        # aggregate "other" abundances
        if self.cbAggResults.isChecked():
            agg_abundance = (
                self.results.iloc[self.sbAggResults.value():].sum())
            agg_abundance["glycoform"] = self.tr("other")
            self.results_agg = (
                self.results.iloc[:self.sbAggResults.value()].append(
                    agg_abundance, ignore_index=True))
        else:
            self.results_agg = self.results

        # extract x- and y-values from series
        x_values = list(self.results_agg["glycoform"].str.split(" or").str[0])
        y_values_obs = list(self.results_agg["abundance"])
        y_values_cor = list(self.results_agg["corr_abundance"])

        # assemble the chart
        bar_set_obs = QBarSet(self.tr("observed"))
        bar_set_obs.append(y_values_obs)
        bar_set_obs.setColor(QColor("#225ea8"))
        bar_set_obs.hovered.connect(self.update_results_label)
        bar_set_cor = QBarSet(self.tr("corrected"))
        bar_set_cor.append(y_values_cor)
        bar_set_cor.setColor(QColor("#41b6c4"))
        bar_set_cor.hovered.connect(self.update_results_label)
        bar_series = QBarSeries()
        bar_series.append([bar_set_obs, bar_set_cor])

        x_axis = QBarCategoryAxis()
        x_axis.append(x_values)
        x_axis.setTitleVisible(False)
        x_axis.setLabelsAngle(270)

        range_min = min(
            self.results_agg[["abundance", "corr_abundance"]].min().min(), 0)
        range_min = math.floor(range_min / 20) * 20
        range_max = (self.results_agg[["abundance",
                                       "corr_abundance"]].max().max())
        range_max = math.ceil(range_max / 20) * 20
        tick_count = (range_max - range_min) // 20 + 1
        y_axis = QValueAxis()
        y_axis.setRange(range_min, range_max)
        y_axis.setTickCount(tick_count)
        y_axis.setTitleText(self.tr("abundance"))
        y_axis.setLabelFormat("%d")

        chart = QChart()
        chart.addSeries(bar_series)
        chart.setAxisX(x_axis, bar_series)
        chart.setAxisY(y_axis, bar_series)
        chart.legend().setVisible(False)
        chart.setBackgroundRoundness(0)
        chart.layout().setContentsMargins(0, 0, 0, 0)
        chart.setMargins(QMargins(5, 5, 5, 5))
        self.cvResults.setChart(chart)
class Ui(mainwindow.Ui_MainWindow):
    def __init__(self, MainWindow):
        super(Ui, self).setupUi(MainWindow)
        self.MainWindow = MainWindow
        self.stix_tctm_parser = stix_parser.StixTCTMParser()
        self.initialize()

    def close(self):
        self.MainWindow.close()

    def style(self):
        return self.MainWindow.style()

    def initialize(self):
        self.tabWidget.setCurrentIndex(0)
        self.actionExit.triggered.connect(self.close)
        self.actionPlot.setEnabled(False)

        self.actionNext.setIcon(self.style().standardIcon(
            QtWidgets.QStyle.SP_ArrowForward))
        self.actionPrevious.setIcon(self.style().standardIcon(
            QtWidgets.QStyle.SP_ArrowBack))
        self.actionOpen.setIcon(self.style().standardIcon(
            QtWidgets.QStyle.SP_DialogOpenButton))
        self.actionSave.setIcon(self.style().standardIcon(
            QtWidgets.QStyle.SP_DriveFDIcon))

        self.actionSave.triggered.connect(self.save)

        self.actionOpen.triggered.connect(self.getOpenFilename)

        self.actionNext.triggered.connect(self.nextPacket)
        self.actionPrevious.triggered.connect(self.previousPacket)
        self.actionAbout.triggered.connect(self.about)

        self.actionPrevious.setEnabled(False)
        self.actionNext.setEnabled(False)
        self.actionSave.setEnabled(False)
        self.actionPlot.setEnabled(False)
        self.actionCopy.triggered.connect(self.onCopyTriggered)

        self.packetTreeWidget.currentItemChanged.connect(self.onPacketSelected)

        self.actionCopy.setEnabled(False)
        self.actionPaste.triggered.connect(self.onPasteTriggered)
        self.actionLog.triggered.connect(self.dockWidget.show)
        self.actionSetIDB.triggered.connect(self.onSetIDBClicked)
        self.plotButton.clicked.connect(self.onPlotButtonClicked)
        self.exportButton.clicked.connect(self.onExportButtonClicked)
        self.actionPlot.triggered.connect(self.onPlotActionClicked)
        self.actionLoadMongodb.triggered.connect(self.onLoadMongoDBTriggered)
        self.actionConnectTSC.triggered.connect(self.onConnectTSCTriggered)
        self.actionPacketFilter.triggered.connect(self.onPacketFilterTriggered)
        self.actionPlugins.triggered.connect(self.onPluginTriggered)
        self.actionOnlineHelp.triggered.connect(self.onOnlineHelpTriggered)
        self.actionViewBinary.triggered.connect(self.onViewBinaryTriggered)

        self.packetTreeWidget.customContextMenuRequested.connect(
            self.packetTreeContextMenuEvent)

        self.mdb = None

        self.current_row = 0
        self.data = []
        self.x = []
        self.y = []
        self.xlabel = 'x'
        self.ylabel = 'y'

        self.buttons_enabled = False

        self.chart = QChart()
        self.chart.layout().setContentsMargins(0, 0, 0, 0)
        self.chart.setBackgroundRoundness(0)
        self.savePlotButton.clicked.connect(self.savePlot)

        self.chartView = QChartView(self.chart)
        self.gridLayout.addWidget(self.chartView, 1, 0, 1, 15)
        #self.packetTreeWidget.itemDoubleClicked.connect(self.onPacketTreeItemDoubleClicked)
        self.selected_services = SELECTED_SERVICES
        self.selected_SPID = []

        # IDB location

        self.settings = QtCore.QSettings('FHNW', 'stix_parser')
        self.idb_filename = self.settings.value('idb_filename', [], str)
        if self.idb_filename:
            idb._stix_idb.reload(self.idb_filename)
        if not idb._stix_idb.is_connected():
            self.showMessage('IDB has not been set!')
        else:
            self.showMessage(
                'IDB location: {} '.format(idb._stix_idb.get_idb_filename()),
                1)

    def packetTreeContextMenuEvent(self, pos):
        menu = QtWidgets.QMenu()
        rawDataAction = menu.addAction('Show raw data')
        menu.addSeparator()
        filterAction = menu.addAction('Filter packets')
        copyPacketAction = menu.addAction('Copy packet')
        menu.addSeparator()
        deleteAllAction = menu.addAction('Delete all packets')
        self.current_row = self.packetTreeWidget.currentIndex().row()

        rawDataAction.triggered.connect(self.onViewBinaryTriggered)
        filterAction.triggered.connect(self.onPacketFilterTriggered)
        copyPacketAction.triggered.connect(self.onCopyTriggered)
        deleteAllAction.triggered.connect(self.onDeleteAllTriggered)
        action = menu.exec_(self.packetTreeWidget.viewport().mapToGlobal(pos))

    def onDeleteAllTriggered(self):
        self.data.clear()
        self.packetTreeWidget.clear()
        self.paramTreeWidget.clear()

    #def onPacketTreeItemDoubleClicked(self):
    #    self.onViewBinaryTriggered()

    def onViewBinaryTriggered(self):
        diag = QtWidgets.QDialog()
        diag_ui = raw_viewer.Ui_Dialog()
        diag_ui.setupUi(diag)
        if self.data:
            try:
                raw = self.data[self.current_row]['bin']
                header = self.data[self.current_row]['header']
                diag_ui.setPacketInfo('{}({},{})  {}'.format(
                    header['TMTC'], header['service_type'],
                    header['service_subtype'], header['DESCR']))
                diag_ui.displayRaw(raw)
            except (IndexError, KeyError):
                diag_ui.setText('Raw data not available.')
        diag.exec_()

    def onOnlineHelpTriggered(self):
        webbrowser.open('https://github.com/i4Ds/STIX-dataviewer', new=2)

    def onPluginTriggered(self):
        self.plugin_location = self.settings.value('plugin_location', [], str)
        diag = QtWidgets.QDialog()
        diag_ui = plugin.Ui_Dialog()
        diag_ui.setupUi(diag)
        if self.plugin_location:
            diag_ui.setPluginLocation(self.plugin_location)

        diag_ui.setData(self.data, self.current_row)
        diag.exec_()
        location = diag_ui.getPluginLocation()
        if location != self.plugin_location:
            self.settings.setValue('plugin_location', location)

    def onPacketFilterTriggered(self):
        diag = QtWidgets.QDialog()
        diag_ui = packet_filter.Ui_Dialog()
        diag_ui.setupUi(diag)
        diag_ui.setSelectedServices(self.selected_services)
        diag_ui.buttonBox.accepted.connect(
            partial(self.applyServiceFilter, diag_ui))
        diag.exec_()

    def applyServiceFilter(self, diag_ui):
        self.selected_SPID = diag_ui.getSelectedSPID()
        self.selected_services = diag_ui.getSelectedServices()
        self.showMessage('Applying filter...')

        self.addPacketsToView(self.data, True, show_stat=False)

    def onExportButtonClicked(self):
        if self.y:
            filename = str(
                QtWidgets.QFileDialog.getSaveFileName(None,
                                                      "Save data to file", "",
                                                      "CSV(*.csv)")[0])
            if filename:
                with open(filename, 'w') as f:
                    f.write('{},{}\n'.format(self.xlabel, self.ylabel))
                    for xx, yy in zip(self.x, self.y):
                        f.write('{},{}\n'.format(xx, yy))
                    self.showMessage(
                        'The data has been written to {}'.format(filename))
        else:
            msgBox = QtWidgets.QMessageBox()
            msgBox.setIcon(QtWidgets.QMessageBox.Information)
            msgBox.setText('Plot first!')
            msgBox.setWindowTitle("Warning")
            msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
            msgBox.exec_()

    def savePlot(self):
        # if self.figure.get_axes():
        if self.chart:
            filetypes = "PNG (*.png);;JPEG (*.jpg)"
            filename = str(
                QtWidgets.QFileDialog.getSaveFileName(None,
                                                      "Save plot to file", "",
                                                      filetypes)[0])
            if filename:
                if not filename.endswith(('.png', '.jpg')):
                    filename += '.png'
                # self.figure.savefig(filename)
                p = self.chartView.grab()
                p.save(filename)
                self.showMessage(('Saved to %s.' % filename))

        else:
            msgBox = QtWidgets.QMessageBox()
            msgBox.setIcon(QtWidgets.QMessageBox.Information)
            msgBox.setText('No figure to save')
            msgBox.setWindowTitle("STIX raw data viewer")
            msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
            msgBox.exec_()

    def onCopyTriggered(self):
        packet_id = self.current_row
        try:
            packet = self.data[packet_id]
            ss = pprint.pformat(packet)
            cb = QtWidgets.QApplication.clipboard()
            cb.clear(mode=cb.Clipboard)
            cb.setText(ss, mode=cb.Clipboard)
            msg = QtWidgets.QMessageBox()
            msg.setIcon(QtWidgets.QMessageBox.Information)
            msg.setText(
                "The data of the selected packet has been copied to the clipboard."
            )
            msg.setWindowTitle("Information")
            msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
            retval = msg.exec_()

        except Exception as e:
            self.showMessage(str(e), 0)

    def onPasteTriggered(self):
        raw_hex = QtWidgets.QApplication.clipboard().text()
        if len(raw_hex) < 16:
            self.showMessage('No data in the clipboard.')
            return
        data_hex = re.sub(r"\s+", "", raw_hex)
        try:
            data_binary = binascii.unhexlify(data_hex)
            packets = self.stix_tctm_parser.parse_binary(data_binary,
                                                         0,
                                                         store_binary=True)
            if not packets:
                return
            self.showMessage('%d packets read from the clipboard' %
                             len(packets))
            self.onDataReady(packets, clear=False, show_stat=False)
        except Exception as e:
            self.showMessageBox(str(e), data_hex)

    def showMessageBox(self, message, content):
        msg = QtWidgets.QMessageBox()
        msg.setIcon(QtWidgets.QMessageBox.Critical)
        msg.setText("Error")
        msg.setInformativeText(message)
        msg.setWindowTitle("Error")
        msg.setDetailedText(content)
        msg.setStandardButtons(QtWidgets.QMessageBox.Ok
                               | QtWidgets.QMessageBox.Cancel)
        retval = msg.exec_()

    def showMessage(self, msg, where=0):
        if where != 1:
            self.statusbar.showMessage(msg)
        if where != 0:
            self.statusListWidget.addItem(msg)
        # if destination !=0 :
        #    self.listWidget_2.addItem(msg)

    def onSetIDBClicked(self):
        self.idb_filename = QtWidgets.QFileDialog.getOpenFileName(
            None, 'Select file', '.', 'IDB file(*.db *.sqlite *.sqlite3)')[0]

        if not self.idb_filename:
            return

        idb._stix_idb.reload(self.idb_filename)
        if idb._stix_idb.is_connected():
            #settings = QtCore.QSettings('FHNW', 'stix_parser')
            self.settings.setValue('idb_filename', self.idb_filename)
        self.showMessage(
            'IDB location: {} '.format(idb._stix_idb.get_idb_filename()), 1)

    def save(self):
        filetypes = 'python compressed pickle (*.pklz);; python pickle file (*.pkl);; binary data (*.dat)'
        self.output_filename = str(
            QtWidgets.QFileDialog.getSaveFileName(None, "Save packets to", "",
                                                  filetypes)[0])

        if not self.output_filename.endswith(('.pklz', '.pkl', '.dat')):
            msg = 'unsupported file format !'
            self.showMessage(msg)
            return

        msg = 'Writing data to file %s' % self.output_filename
        self.showMessage(msg)

        if self.output_filename.endswith(('.pklz', '.pkl')):
            stw = stix_writer.StixPickleWriter(self.output_filename)
            stw.register_run(str(self.input_filename))
            stw.write_all(self.data)
            #stw.done()
        elif self.output_filename.endswith('.dat'):
            stw = stix_writer.StixBinaryWriter(self.output_filename)
            stw.write_all(self.data)
            num_ok = stw.get_num_sucess()
            msg = (
                'The binary data of {} packets written to file {}, total packets {}'
                .format(num_ok, self.output_filename, len(self.data)))
            self.showMessage(msg)

    def setListViewSelected(self, row):
        #index = self.model.createIndex(row, 0);
        # if index.isValid():
        #    self.model.selectionModel().select( index, QtGui.QItemSelectionModel.Select)
        pass

    def about(self):
        msgBox = QtWidgets.QMessageBox()
        msgBox.setIcon(QtWidgets.QMessageBox.Information)
        msgBox.setText("STIX raw data parser and viewer, [email protected]")
        msgBox.setWindowTitle("Stix data viewer")

        msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
        msgBox.exec_()

    def nextPacket(self):
        self.current_row += 1
        length = len(self.data)

        if self.current_row >= length:
            self.current_row = length - 1
            self.showMessage('No more packet!')

        self.showPacket(self.current_row)
        self.setListViewSelected(self.current_row)

    def previousPacket(self):
        self.current_row -= 1
        if self.current_row < 0:
            self.current_row = 0
            self.showMessage('Reach the first packet!')

        self.showPacket(self.current_row)
        self.setListViewSelected(self.current_row)

    def getOpenFilename(self):
        filetypes = (
            'STIX raw data(*.dat *.bin *.binary);; python pickle files (*.pkl *pklz);;'
            'ESA xml files (*xml);;'
            'ESA ascii files(*.ascii);; CMDVS archive files (*.BDF);; All(*)')
        self.input_filename = QtWidgets.QFileDialog.getOpenFileName(
            None, 'Select file', '.', filetypes)[0]
        if not self.input_filename:
            return
        self.openFile(self.input_filename)

    def openFile(self, filename):
        self.packetTreeWidget.clear()
        msg = 'Loading file %s ...' % filename
        self.showMessage(msg)
        self.dataReader = StixFileReader(filename)
        #self.dataReader.dataLoaded.connect(self.onDataReady)
        self.dataReader.packetArrival.connect(self.onPacketArrival)
        self.dataReader.error.connect(self.onDataReaderError)
        self.dataReader.info.connect(self.onDataReaderInfo)
        self.dataReader.warn.connect(self.onDataReaderWarn)
        self.dataReader.start()

    def onDataReaderInfo(self, msg):
        self.showMessage(msg, 0)

    def onDataReaderWarn(self, msg):
        self.showMessage(msg, 1)

    def onDataReaderError(self, msg):
        self.showMessage(msg, 1)

    def onDataReady(self, data, clear=True, show_stat=True):
        if not clear:
            self.data.extend(data)
        else:
            self.data = data
        if data:
            self.addPacketsToView(data, clear=clear, show_stat=show_stat)
            self.enableButtons()

    def enableButtons(self):
        if not self.buttons_enabled:
            self.actionPrevious.setEnabled(True)
            self.actionNext.setEnabled(True)
            self.actionSave.setEnabled(True)
            self.actionCopy.setEnabled(True)
            self.actionPlot.setEnabled(True)
            self.actionViewBinary.setEnabled(True)
            self.buttons_enabled = True

    def addPacketsToView(self, data, clear=True, show_stat=True):
        if clear:
            self.packetTreeWidget.clear()
        #t0 = 0
        for p in data:
            if type(p) is not dict:
                continue
            header = p['header']

            root = QtWidgets.QTreeWidgetItem(self.packetTreeWidget)
            # if t0 == 0:
            #    t0 = header['time']
            timestamp_str = ''
            try:
                timestamp_str = header['utc']
            except KeyError:
                timestamp_str = '{:.2f}'.format(header['time'])
                #- t0)

            root.setText(0, timestamp_str)
            root.setText(
                1,
                '{}({},{}) - {}'.format(header['TMTC'], header['service_type'],
                                        header['service_subtype'],
                                        header['DESCR']))

            if not self.selected_SPID:
                #not set then apply service
                if header['service_type'] not in self.selected_services:
                    root.setHidden(True)
            else:
                if header['SPID'] not in self.selected_SPID:
                    root.setHidden(True)

        if show_stat:
            total_packets = len(self.data)
            self.showMessage(('Total packet(s): %d' % total_packets))

    def onConnectTSCTriggered(self):
        diag = QtWidgets.QDialog()
        diag_ui = tsc_connection.Ui_Dialog()
        diag_ui.setupUi(diag)
        self.tsc_host = self.settings.value('tsc_host', [], str)
        self.tsc_port = self.settings.value('tsc_port', [], str)
        if self.tsc_host:
            diag_ui.serverLineEdit.setText(self.tsc_host)
        if self.tsc_port:
            diag_ui.portLineEdit.setText(self.tsc_port)
        diag_ui.buttonBox.accepted.connect(partial(self.connectToTSC, diag_ui))
        diag.exec_()

    def connectToTSC(self, dui):
        host = dui.serverLineEdit.text()
        port = dui.portLineEdit.text()
        self.showMessage('Connecting to TSC...')
        self.socketPacketReceiver = StixSocketPacketReceiver(host, int(port))
        self.socketPacketReceiver.packetArrival.connect(self.onPacketArrival)
        self.socketPacketReceiver.error.connect(self.onDataReaderError)
        self.socketPacketReceiver.info.connect(self.onDataReaderInfo)
        self.socketPacketReceiver.warn.connect(self.onDataReaderWarn)
        self.socketPacketReceiver.start()

    def onPacketArrival(self, packets):
        if packets:
            self.onDataReady(packets, clear=False, show_stat=True)

    def onLoadMongoDBTriggered(self):
        diag = QtWidgets.QDialog()
        diag_ui = mongo_dialog.Ui_Dialog()
        diag_ui.setupUi(diag)
        #self.settings = QtCore.QSettings('FHNW', 'stix_parser')
        self.mongo_server = self.settings.value('mongo_server', [], str)
        self.mongo_port = self.settings.value('mongo_port', [], str)
        self.mongo_user = self.settings.value('mongo_user', [], str)
        self.mongo_pwd = self.settings.value('mongo_pwd', [], str)

        if self.mongo_server:
            diag_ui.serverLineEdit.setText(self.mongo_server)
        if self.mongo_port:
            diag_ui.portLineEdit.setText(self.mongo_port)

        if self.mongo_user:
            diag_ui.userLineEdit.setText(self.mongo_user)
        if self.mongo_pwd:
            diag_ui.pwdLineEdit.setText(self.mongo_pwd)

        diag_ui.pushButton.clicked.connect(
            partial(self.loadRunsFromMongoDB, diag_ui))
        diag_ui.buttonBox.accepted.connect(
            partial(self.loadDataFromMongoDB, diag_ui, diag))
        diag.exec_()

    def loadRunsFromMongoDB(self, dui):
        server = dui.serverLineEdit.text()
        port = dui.portLineEdit.text()
        user = dui.userLineEdit.text()
        pwd = dui.pwdLineEdit.text()

        self.showMessage('saving setting...')
        if self.mongo_server != server:
            self.settings.setValue('mongo_server', server)
        if self.mongo_port != port:
            self.settings.setValue('mongo_port', port)

        if self.mongo_user != user:
            self.settings.setValue('mongo_user', user)
        if self.mongo_pwd != pwd:
            self.settings.setValue('mongo_pwd', pwd)

        self.showMessage('connecting Mongo database ...')
        self.mdb = mgdb.MongoDB(server, int(port), user, pwd)
        if not self.mdb.is_connected():
            return

        dui.treeWidget.clear()
        self.showMessage('Fetching data...')
        for run in self.mdb.get_runs():
            root = QtWidgets.QTreeWidgetItem(dui.treeWidget)
            root.setText(0, str(run['_id']))
            root.setText(1, run['filename'])
            root.setText(2, run['date'])
            root.setText(3, str(run['start']))
            root.setText(4, str(run['end']))

    def loadDataFromMongoDB(self, dui, diag):
        self.showMessage('Loading packets ...')
        selected_runs = []
        for item in dui.treeWidget.selectedItems():
            selected_runs.append(item.text(0))
        if not selected_runs:
            self.showMessage('Run not selected!')
        if selected_runs:
            diag.done(0)
            self.showMessage('Loading data ...!')
            data = self.mdb.get_packets(selected_runs[0])
            if data:
                self.onDataReady(data, clear=True)
            else:
                self.showMessage('No packets found!')
        # close

    def onPacketSelected(self, cur, pre):
        self.current_row = self.packetTreeWidget.currentIndex().row()
        self.showMessage(('Packet #%d selected' % self.current_row))
        self.showPacket(self.current_row)

    def showPacket(self, row):
        if not self.data:
            return
        header = self.data[row]['header']
        total_packets = len(self.data)
        self.showMessage(
            ('Packet %d / %d  %s ' % (row, total_packets, header['DESCR'])))
        self.paramTreeWidget.clear()
        header_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget)
        header_root.setText(0, "Header")
        rows = len(header)
        for key, val in header.items():
            root = QtWidgets.QTreeWidgetItem(header_root)
            root.setText(0, key)
            root.setText(1, str(val))

        params = self.data[row]['parameters']
        param_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget)
        param_root.setText(0, "Parameters")
        self.showParameterTree(params, param_root)
        self.paramTreeWidget.expandItem(param_root)
        self.paramTreeWidget.expandItem(header_root)
        self.current_row = row

    def showParameterTree(self, params, parent):
        if not params:
            return
        for p in params:
            root = QtWidgets.QTreeWidgetItem(parent)
            if not p:
                continue
            try:
                param_name = p['name']
                desc = ''  #description of parameter
                if 'desc' in p:
                    desc = p['desc']
                if not desc:
                    desc = idb._stix_idb.get_PCF_description(param_name)
                scos_desc = idb._stix_idb.get_scos_description(param_name)
                if scos_desc:
                    root.setToolTip(1, scos_desc)
                root.setText(0, param_name)
                root.setText(1, desc)
                root.setText(2, str(p['raw']))
                try:
                    root.setToolTip(2, hex(p['raw'][0]))
                except:
                    pass
                root.setText(3, str(p['eng']))
                if 'children' in p:
                    if p['children']:
                        self.showParameterTree(p['children'], root)
            except KeyError:
                self.showMessage(
                    '[Error  ]: keyError occurred when adding parameter')
        self.paramTreeWidget.itemDoubleClicked.connect(self.onTreeItemClicked)

    def walk(self, name, params, header, ret_x, ret_y, xaxis=0, data_type=0):
        if not params:
            return
        timestamp = header['time']
        for p in params:
            if type(p) is not dict:
                continue
            if name == p['name']:
                values = None
                #print('data type:{}'.format(data_type))
                if data_type == 0:
                    values = p['raw']
                else:
                    values = p['eng']
                try:
                    yvalue = None
                    if (type(values) is tuple) or (type(values) is list):
                        yvalue = float(values[0])
                    else:
                        yvalue = float(values)
                    ret_y.append(yvalue)
                    if xaxis == 1:
                        ret_x.append(timestamp)
                    else:
                        self.showMessage(('Can not plot %s  ' % str(yvalue)))
                except Exception as e:
                    self.showMessage(('%s ' % str(e)))

            if 'children' in p:
                if p['children']:
                    self.walk(name, p['children'], header, ret_x, ret_y, xaxis,
                              data_type)

    def onPlotButtonClicked(self):
        if self.chart:
            self.chart.removeAllSeries()
        if not self.data:
            return
        self.showMessage('Preparing plot ...')
        name = self.paramNameEdit.text()
        packet_selection = self.comboBox.currentIndex()
        xaxis_type = self.xaxisComboBox.currentIndex()
        data_type = self.dataTypeComboBox.currentIndex()

        timestamp = []
        self.y = []
        packet_id = self.current_row
        params = self.data[packet_id]['parameters']
        header = self.data[packet_id]['header']
        current_spid = header['SPID']
        if packet_selection == 0:
            self.walk(name, params, header, timestamp, self.y, xaxis_type,
                      data_type)
        elif packet_selection == 1:
            for packet in self.data:
                header = packet['header']
                if packet['header']['SPID'] != current_spid:
                    continue
                params = packet['parameters']
                self.walk(name, params, header, timestamp, self.y, xaxis_type,
                          data_type)

        self.x = []

        if not self.y:
            self.showMessage('No data points')
        elif self.y:
            style = self.styleEdit.text()
            if not style:
                style = '-'
            title = '%s' % str(name)
            desc = self.descLabel.text()
            if desc:
                title += '- %s' % desc

            self.chart.setTitle(title)

            ylabel = 'Raw value'
            xlabel = name
            if data_type == 1:
                ylabel = 'Engineering  value'
            if xaxis_type == 0:
                if packet_selection == 1:
                    xlabel = "Packet #"
                else:
                    xlabel = "Repeat #"
                self.x = range(0, len(self.y))
            if xaxis_type == 1:
                self.x = [t - timestamp[0] for t in timestamp]
                xlabel = 'Time -T0 (s)'

            if xaxis_type != 2:
                series = QLineSeries()
                series2 = None
                for xx, yy in zip(self.x, self.y):
                    series.append(xx, yy)
                if 'o' in style:
                    series2 = QScatterSeries()
                    for xx, yy in zip(self.x, self.y):
                        series2.append(xx, yy)
                    self.chart.addSeries(series2)
                self.chart.addSeries(series)
                axisX = QValueAxis()
                axisX.setTitleText(xlabel)
                axisY = QValueAxis()
                axisY.setTitleText(ylabel)

                self.chart.setAxisX(axisX)
                self.chart.setAxisY(axisY)
                series.attachAxis(axisX)
                series.attachAxis(axisY)
            else:
                nbins = len(set(self.y))
                ycounts, xedges = np.histogram(self.y, bins=nbins)
                series = QLineSeries()
                for i in range(0, nbins):
                    meanx = (xedges[i] + xedges[i + 1]) / 2.
                    series.append(meanx, ycounts[i])
                self.chart.addSeries(series)
                axisX = QValueAxis()
                axisX.setTitleText(name)
                axisY = QValueAxis()
                axisY.setTitleText("Counts")

                self.chart.setAxisY(axisY)
                self.chart.setAxisX(axisX)
                series.attachAxis(axisX)
                series.attachAxis(axisY)

            self.xlabel = xlabel
            self.ylabel = ylabel
            self.chartView.setRubberBand(QChartView.RectangleRubberBand)
            self.chartView.setRenderHint(QtGui.QPainter.Antialiasing)
            msg = 'Y length: {}, min-Y:  {}, max-Y: {}'.format(
                len(self.y), min(self.y), max(self.y))

            self.showMessage(msg, 1)

            self.showMessage('The canvas updated!')

    def plotParameter(self, name=None, desc=None):
        self.tabWidget.setCurrentIndex(1)
        if name:
            self.paramNameEdit.setText(name)
        if desc:
            self.descLabel.setText(desc)

    def onPlotActionClicked(self):
        self.tabWidget.setCurrentIndex(1)
        self.plotParameter()

    def onTreeItemClicked(self, it, col):
        self.plotParameter(it.text(0), it.text(1))