Esempio n. 1
0
class SiriusTimePlot(PyDMTimePlot):
    """PyDMTimePlot with some extra features."""

    bufferReset = Signal()
    timeSpanChanged = Signal()

    def __init__(self, *args, show_tooltip=False, **kws):
        super().__init__(*args, **kws)
        self._filled_with_arch_data = dict()
        self._show_tooltip = show_tooltip

        self.vb2 = ViewBox()
        self.plotItem.scene().addItem(self.vb2)
        self.vb2.setXLink(self.plotItem)
        self.plotItem.getAxis('right').linkToView(self.vb2)
        self._updateViews()
        self.plotItem.vb.sigResized.connect(self._updateViews)

        self.carch = None

        # show auto adjust button
        self.plotItem.showButtons()

        # use pan mouse mode (3-button)
        self.plotItem.getViewBox().setMouseMode(ViewBox.PanMode)

        # connect sigMouseMoved
        self.plotItem.scene().sigMouseMoved.connect(self._handle_mouse_moved)

        # add new actions to menu
        rst_act = QAction("Clear buffers")
        rst_act.triggered.connect(self._resetBuffers)
        tsp_act = QAction("Change time span")
        tsp_act.triggered.connect(self._changeTimeSpan)
        self.plotItem.scene().contextMenu.extend([rst_act, tsp_act])

    @Property(bool)
    def showToolTip(self):
        """
        Whether to show or not tooltip with curve values.

        Returns
        -------
        use : bool
            Tooltip enable status in use
        """
        return self._show_tooltip

    @showToolTip.setter
    def showToolTip(self, new_show):
        """
        Whether to show or not tooltip with curve values.

        Parameters
        ----------
        new_show : bool
            The new tooltip enable status to use
        """
        self._show_tooltip = new_show

    def addCurve(self, plot_item, axis='left', curve_color=None):
        """Reimplement to use right axis."""
        if curve_color is None:
            curve_color = utilities.colors.default_colors[len(
                self._curves) % len(utilities.colors.default_colors)]
            plot_item.color_string = curve_color
        self._curves.append(plot_item)
        if axis == 'left':
            self.plotItem.addItem(plot_item)
        elif axis == 'right':
            if not self.plotItem.getAxis('right').isVisible():
                self.plotItem.showAxis('right')
            self.vb2.addItem(plot_item)
        else:
            raise ValueError('Choose a valid axis!')

        # Connect channels
        for chan in plot_item.channels():
            if chan:
                chan.connect()

    def addYChannel(self,
                    y_channel=None,
                    name=None,
                    color=None,
                    lineStyle=None,
                    lineWidth=None,
                    symbol=None,
                    symbolSize=None,
                    axis='left'):
        """Reimplement to use SiriusTimePlotItem and right axis."""
        plot_opts = dict()
        plot_opts['symbol'] = symbol
        if symbolSize is not None:
            plot_opts['symbolSize'] = symbolSize
        if lineStyle is not None:
            plot_opts['lineStyle'] = lineStyle
        if lineWidth is not None:
            plot_opts['lineWidth'] = lineWidth

        # Add curve
        new_curve = SiriusTimePlotItem(
            self,
            y_channel,
            plot_by_timestamps=self._plot_by_timestamps,
            name=name,
            color=color,
            **plot_opts)
        new_curve.setUpdatesAsynchronously(self.updatesAsynchronously)
        new_curve.setBufferSize(self._bufferSize, initialize_buffer=True)

        self.update_timer.timeout.connect(new_curve.asyncUpdate)
        self.addCurve(new_curve, axis, curve_color=color)

        new_curve.data_changed.connect(self.set_needs_redraw)
        self.redraw_timer.start()

        return new_curve

    def updateXAxis(self, update_immediately=False):
        """Reimplement to show only existing range."""
        if len(self._curves) == 0:
            return

        if self._plot_by_timestamps:
            if self._update_mode == PyDMTimePlot.SynchronousMode:
                maxrange = max([curve.max_x() for curve in self._curves])
            else:
                maxrange = time.time()

            mini = Time.now().timestamp()
            for curve in self._curves:
                firstvalid = (curve.data_buffer[0] != 0).argmax()
                if curve.data_buffer[0, firstvalid] == 0:
                    continue
                mini = min(mini, curve.data_buffer[0, firstvalid])
            minrange = max(maxrange - self._time_span, mini)

            self.plotItem.setXRange(minrange,
                                    maxrange,
                                    padding=0.0,
                                    update=update_immediately)
        else:
            diff_time = self.starting_epoch_time - \
                max([curve.max_x() for curve in self._curves])
            if diff_time > DEFAULT_X_MIN:
                diff_time = DEFAULT_X_MIN
            self.getViewBox().setLimits(minXRange=diff_time)

    def _updateViews(self):
        self.vb2.setGeometry(self.plotItem.vb.sceneBoundingRect())
        self.vb2.linkedViewChanged(self.plotItem.vb, self.vb2.XAxis)

    def _get_value_from_arch(self, pvname, t_init, t_end, process_type,
                             process_bin_intvl):
        """Get values from archiver."""
        if self.carch is None:
            self.carch = ClientArchiver()
        self.carch.timeout = 120
        data = self.carch.getData(pvname, t_init, t_end, process_type,
                                  process_bin_intvl)
        if not data:
            return
        return data['timestamp'], data['value']

    def fill_curve_with_archdata(self,
                                 curve,
                                 pvname,
                                 t_init,
                                 t_end,
                                 factor=None,
                                 process_type='',
                                 process_bin_intvl=None):
        """Fill curve with archiver data."""
        data = self._get_value_from_arch(pvname, t_init, t_end, process_type,
                                         process_bin_intvl)
        if not data:
            return
        datax, datay = data
        self.fill_curve_buffer(curve, datax, datay, factor)

        self._filled_with_arch_data[pvname] = dict(
            curve=curve,
            factor=factor,
            process_type=process_type,
            process_bin_intvl=process_bin_intvl)

    def fill_curve_buffer(self, curve, datax, datay, factor=None):
        """Fill curve buffer."""
        nrpts = len(datax)
        if not nrpts:
            return
        buff = _np.zeros((2, self.bufferSize), order='f', dtype=float)
        if nrpts > self.bufferSize:
            smpls2discard = nrpts - self.bufferSize
            datax = datax[smpls2discard:]
            datay = datay[smpls2discard:]
            nrpts = len(datax)
        firstsmpl2fill = self.bufferSize - nrpts
        buff[0, firstsmpl2fill:] = datax
        buff[1, firstsmpl2fill:] = datay
        if factor:
            buff[1, :] /= factor
        curve.data_buffer = buff
        curve.points_accumulated = nrpts
        curve._min_y_value = min(datay)
        curve._max_y_value = max(datay)
        curve.latest_value = datay[-1]

    def _resetBuffers(self):
        for curve in self._curves:
            curve.initialize_buffer()
        self.bufferReset.emit()

    def _changeTimeSpan(self):
        new_time_span, ok = QInputDialog.getInt(
            self, 'Input', 'Set new time span value [s]: ')
        if not ok:
            return

        if new_time_span > self.timeSpan:
            t_end = Time.now()
            t_init = t_end - new_time_span
            for pvname, info in self._filled_with_arch_data.items():
                self.fill_curve_with_archdata(info['curve'], pvname,
                                              t_init.get_iso8601(),
                                              t_end.get_iso8601(),
                                              info['factor'],
                                              info['process_type'],
                                              info['process_bin_intvl'])

        self.timeSpan = new_time_span
        self.timeSpanChanged.emit()

    def _handle_mouse_moved(self, pos):
        """Show tooltip at mouse move."""
        if not self._show_tooltip:
            return

        # create label tooltip, if needed
        if not hasattr(self, 'label_tooltip'):
            self.label_tooltip = QLabel(self, Qt.ToolTip)
            self.timer_tooltip = QTimer(self)
            self.timer_tooltip.timeout.connect(self.label_tooltip.hide)
            self.timer_tooltip.setInterval(1000)

        # find nearest curve point
        nearest = (self._curves[0], _np.inf, None, None)
        for idx, curve in enumerate(self._curves):
            if not curve.isVisible():
                continue
            mappos = curve.mapFromScene(pos)
            posx, posy = mappos.x(), mappos.y()
            xData, yData = curve.curve.xData, curve.curve.yData
            if not xData.size:
                continue
            diffx = xData - posx
            idx = _np.argmin(_np.abs(diffx))
            if diffx[idx] < 0.5:
                valx, valy = xData[idx], yData[idx]
                diffy = abs(valy - posy)
                if diffy < nearest[1]:
                    nearest = (curve, diffy, valx, valy)

        # show tooltip
        curve, diffy, valx, valy = nearest
        ylimts = self.getViewBox().state['viewRange'][1]
        ydelta = ylimts[1] - ylimts[0]
        if diffy < 1e-2 * ydelta:
            txt = Time(timestamp=valx).get_iso8601() + '\n'
            txt += f'{curve.name()}: {valy:.3f}'
            font = QApplication.instance().font()
            font.setPointSize(font.pointSize() - 10)
            palette = QPalette()
            palette.setColor(QPalette.WindowText, curve.color)
            self.label_tooltip.setText(txt)
            self.label_tooltip.setFont(font)
            self.label_tooltip.setPalette(palette)
            self.label_tooltip.move(self.mapToGlobal(pos.toPoint()))
            self.label_tooltip.show()
            self.timer_tooltip.start()
            curve.scatter.setData(pos=[
                (valx, valy),
            ],
                                  symbol='o',
                                  size=15,
                                  brush=mkBrush(curve.color))
            curve.scatter.show()
Esempio n. 2
0
class MDIChildPlot(PlotWidget):
    LINE_COLORS: List[str] = ['r', 'g', 'b', 'c', 'm', 'y', 'w']
    AXES_NAMES: Dict[int, str] = {2: 'bottom', 3: 'left', 4: 'right'}

    child_number: int = 1

    def __init__(self, **kwargs):
        super(MDIChildPlot, self).__init__(**kwargs)

        self.setAttribute(Qt.WA_DeleteOnClose)

        self.is_untitled: bool = True
        self.is_modified: bool = False

        self.cur_file: str = ''

        self.curves = []

        self.plotItem2 = ViewBox()
        self.plotItem.showAxis('right')
        self.plotItem.scene().addItem(self.plotItem2)
        self.plotItem.getAxis('right').linkToView(self.plotItem2)
        self.plotItem2.setXLink(self.plotItem)
        self.plotItem.showButtons()
        self.plotItem.showGrid(x=True, y=True)

        # Handle view resizing
        def update_views():
            # view has resized; update auxiliary views to match
            self.plotItem2.setGeometry(self.plotItem.vb.sceneBoundingRect())

            # need to re-update linked axes since this was called
            # incorrectly while views had different shapes.
            # (probably this should be handled in ViewBox.resizeEvent)
            self.plotItem2.linkedViewChanged(self.plotItem.vb, self.plotItem2.XAxis)

        update_views()
        self.plotItem.vb.sigResized.connect(update_views)

        # add items to the context menu
        self.plotItem.vb.menu.addSeparator()
        self.delete_curve_menu: QMenu = QMenu('Delete')
        self.copy_curve_menu: QMenu = QMenu('Copy')
        self.paste_curve_menu: QMenu = QMenu('Paste')
        menus = {self.delete_curve_menu:
                 [('Last Curve…', self.delete_last_curve),
                  ('Curves No.…', self.delete_curves),
                  ('All Curves…', self.delete_all_curves)],
                 self.copy_curve_menu:
                 [('Last Curve…', self.copy_last_curve),
                  ('Curves No.…', self.copy_curves),
                  ('All Curves…', self.copy_all_curves)],
                 }
        for parent_menu, actions in menus.items():
            for title, callback in actions:
                new_action: QAction = QAction(title, parent_menu)
                parent_menu.addAction(new_action)
                new_action.triggered.connect(callback)
        self.plotItem.vb.menu.addMenu(self.delete_curve_menu)
        self.plotItem.vb.menu.addMenu(self.copy_curve_menu)
        self.plotItem.vb.menu.addMenu(self.paste_curve_menu)

        # hide buggy menu items
        for undesired_menu_item_index in (5, 2, 1):
            self.plotItem.subMenus.pop(undesired_menu_item_index)
        self.plotItem.subMenus[1].actions()[0].defaultWidget().children()[1].hide()

    def new_file(self):
        self.is_untitled = True
        self.cur_file = f'Plot {MDIChildPlot.child_number:d}'
        MDIChildPlot.child_number += 1
        self.setWindowTitle(self.cur_file + '[*]')
        self.setWindowModified(True)

        # self.sig.connect(self.document_was_modified)

    def load_irtecon_file(self, file_name: str):
        file = QFile(file_name)
        if not file.open(QFile.ReadOnly | QFile.Text):
            QMessageBox.warning(self, 'MDI',
                                f'Cannot read file {file_name}:\n{file.errorString()}.')
            return False

        QApplication.setOverrideCursor(Qt.WaitCursor)
        in_str = QTextStream(file).readAll()
        file_data = IRTECONFile(in_str)
        self.plotItem.setTitle(file_data.sample_name)
        self.plotItem.addLegend()
        for index, curve in enumerate(file_data.curves):
            self.curves.append(self.plotItem.plot(curve.data[..., :2],
                                                  name=curve.legend_key,
                                                  pen=mkPen(self.LINE_COLORS[index % len(self.LINE_COLORS)])))
        for ax in self.AXES_NAMES.values():
            self.plotItem.hideAxis(ax)
        for ax in file_data.axes:
            if ax.axis in self.AXES_NAMES:
                self.plotItem.showAxis(self.AXES_NAMES[ax.axis])
                self.plotItem.setLabel(self.AXES_NAMES[ax.axis], ax.name, ax.unit)
        QApplication.restoreOverrideCursor()

        self.set_current_file(file_name)

        # self.document().contentsChanged.connect(self.document_was_modified)

        return True

    def delete_last_curve(self):
        if not self.curves:
            return
        ret = QMessageBox.warning(self, 'MDI',
                                  'Do you want to delete the last curve?',
                                  QMessageBox.Yes | QMessageBox.No)
        if ret == QMessageBox.Yes:
            self.plotItem.legend.removeItem(self.curves[-1])
            self.curves[-1].clear()
            del self.curves[-1]
            self.is_modified = True
            self.setWindowTitle(self.user_friendly_current_file() + '[*]')
            self.setWindowModified(True)

    def delete_curves(self):
        def parse_range() -> List[int]:
            # https://stackoverflow.com/a/4248689/8554611
            result = set()
            for part in ranges.split(','):
                x = part.split('-')
                result.update(list(range(int(x[0]), int(x[-1]) + 1)))
            return sorted(result)
        if not self.curves:
            return
        ranges, ok = QInputDialog.getText(self, 'Delete Curves', 'Curves No.:')
        if ok:
            for index in reversed(parse_range()):
                index -= 1
                if index in range(len(self.curves)):
                    self.plotItem.legend.removeItem(self.curves[index])
                    self.curves[index].clear()
                    del self.curves[index]
            self.is_modified = True
            self.setWindowTitle(self.user_friendly_current_file() + '[*]')
            self.setWindowModified(True)

    def delete_all_curves(self):
        if not self.curves:
            return
        ret = QMessageBox.question(self, 'MDI',
                                   'Do you want to delete all the curves?',
                                   QMessageBox.Yes | QMessageBox.No)
        if ret == QMessageBox.Yes:
            while self.curves:
                self.plotItem.legend.removeItem(self.curves[-1])
                self.curves[-1].clear()
                del self.curves[-1]
            self.is_modified = True
            self.setWindowTitle(self.user_friendly_current_file() + '[*]')
            self.setWindowModified(True)

    def copy_last_curve(self):
        if not self.curves:
            return
        raise NotImplementedError

    def copy_curves(self, ranges: str):
        def parse_range() -> List[int]:
            # https://stackoverflow.com/a/4248689/8554611
            result = set()
            for part in ranges.split(','):
                x = part.split('-')
                result.update(list(range(int(x[0]), int(x[-1]) + 1)))
            return sorted(result)
        if not self.curves:
            return
        parse_range()
        raise NotImplementedError

    def copy_all_curves(self):
        if not self.curves:
            return
        raise NotImplementedError

    def save(self):
        if self.is_untitled:
            return self.save_as()
        else:
            return self.save_file(self.cur_file)

    def save_as(self):
        file_name, _ = QFileDialog.getSaveFileName(self, 'Save As',
                                                   self.cur_file)
        if not file_name:
            return False

        return self.save_file(file_name)

    def save_file(self, file_name: str):
        file = QFile(file_name)

        if not file.open(QFile.WriteOnly | QFile.Text):
            QMessageBox.warning(self, 'MDI',
                                f'Cannot write file {file_name}:\n{file.errorString()}.')
            return False

        out_str = QTextStream(file)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        out_str << self.toPlainText()
        QApplication.restoreOverrideCursor()

        self.set_current_file(file_name)
        return True

    def user_friendly_current_file(self):
        def stripped_name(full_file_name):
            return QFileInfo(full_file_name).fileName()

        return stripped_name(self.cur_file)

    def current_file(self):
        return self.cur_file

    def close_event(self, event):
        if self.maybe_save():
            event.accept()
        else:
            event.ignore()

    def maybe_save(self):
        if self.document().isModified():
            ret = QMessageBox.warning(self, 'MDI',
                                      f'"{self.user_friendly_current_file()}" has been modified.\n'
                                      'Do you want to save your changes?',
                                      QMessageBox.Save | QMessageBox.Discard |
                                      QMessageBox.Cancel)
            if ret == QMessageBox.Save:
                return self.save()
            elif ret == QMessageBox.Cancel:
                return False

        return True

    def set_current_file(self, file_name):
        self.cur_file = QFileInfo(file_name).canonicalFilePath()
        self.is_untitled = False
        self.is_modified = False
        self.setWindowModified(False)
        self.setWindowTitle(self.user_friendly_current_file())
class DataWidget(QWidget):
    def __init__(self, parent, shared_data):
        super(DataWidget, self).__init__(parent)
        self.__shared_data = shared_data
        self.__shared_data.update_sync.emit()

        # Add the file selection controls
        self.__dir_picker_button = QPushButton()
        self.__dir_picker_button.setEnabled(True)
        self.__dir_picker_button.setText("Load data")
        self.__dir_picker_button.setIcon(self.style().standardIcon(QStyle.SP_DirIcon))
        self.__dir_picker_button.setToolTip('Select the directory using the file explorer')
        self.__dir_picker_button.clicked.connect(self.__open_dir_picker)

        # Add the sync controls
        self.__sync_time_label = QLabel()
        self.__sync_time_label.setText('Enter the timecode (HH:mm:ss:zzz) : ')

        self.__sync_time_edit = QTimeEdit()
        self.__sync_time_edit.setDisplayFormat('HH:mm:ss:zzz')
        self.__sync_time_edit.setEnabled(False)

        self.__sync_time_button = QPushButton()
        self.__sync_time_button.setText('Sync data')
        self.__sync_time_button.setEnabled(False)
        self.__sync_time_button.clicked.connect(self.__sync_data)

        # Create the layout for the file controls
        dir_layout = QHBoxLayout()
        dir_layout.setContentsMargins(0, 0, 0, 0)
        dir_layout.addWidget(self.__dir_picker_button)
        dir_layout.addStretch(1)
        dir_layout.addWidget(self.__sync_time_label)
        dir_layout.addWidget(self.__sync_time_edit)
        dir_layout.addWidget(self.__sync_time_button)

        # Create the axis and their viewbox
        self.__x_axis_item = AxisItem('left')
        self.__y_axis_item = AxisItem('left')
        self.__z_axis_item = AxisItem('left')

        self.__x_axis_viewbox = ViewBox()
        self.__y_axis_viewbox = ViewBox()
        self.__z_axis_viewbox = ViewBox()

        # Create the widget which will display the data
        self.__graphic_view = GraphicsView(background="#ecf0f1")
        self.__graphic_layout = GraphicsLayout()
        self.__graphic_view.setCentralWidget(self.__graphic_layout)

        # Add the axis to the widget
        self.__graphic_layout.addItem(self.__x_axis_item, row=2, col=3, rowspan=1, colspan=1)
        self.__graphic_layout.addItem(self.__y_axis_item, row=2, col=2, rowspan=1, colspan=1)
        self.__graphic_layout.addItem(self.__z_axis_item, row=2, col=1, rowspan=1, colspan=1)

        self.__plot_item = PlotItem()
        self.__plot_item_viewbox = self.__plot_item.vb
        self.__graphic_layout.addItem(self.__plot_item, row=2, col=4, rowspan=1, colspan=1)

        self.__graphic_layout.scene().addItem(self.__x_axis_viewbox)
        self.__graphic_layout.scene().addItem(self.__y_axis_viewbox)
        self.__graphic_layout.scene().addItem(self.__z_axis_viewbox)

        self.__x_axis_item.linkToView(self.__x_axis_viewbox)
        self.__y_axis_item.linkToView(self.__y_axis_viewbox)
        self.__z_axis_item.linkToView(self.__z_axis_viewbox)

        self.__x_axis_viewbox.setXLink(self.__plot_item_viewbox)
        self.__y_axis_viewbox.setXLink(self.__plot_item_viewbox)
        self.__z_axis_viewbox.setXLink(self.__plot_item_viewbox)

        self.__plot_item_viewbox.sigResized.connect(self.__update_views)
        self.__x_axis_viewbox.enableAutoRange(axis=ViewBox.XAxis, enable=True)
        self.__y_axis_viewbox.enableAutoRange(axis=ViewBox.XAxis, enable=True)
        self.__z_axis_viewbox.enableAutoRange(axis=ViewBox.XAxis, enable=True)

        # Create the final layout
        self.__v_box = QVBoxLayout()
        self.__v_box.addLayout(dir_layout)
        self.__v_box.addWidget(self.__graphic_view)

        self.setLayout(self.__v_box)

        self.__restore_state()

    def __open_dir_picker(self):
        self.__shared_data.data_file_path = QFileDialog.getOpenFileUrl(self, 'Open the Hexoskin data directory',
                                                                       QDir.homePath())[0]
        if self.__shared_data.data_file_path is not None:
            try:
                self.__load_data()
                self.__add_selector_acc_gyr()
                self.__show_data('ACC_X', 'ACC_Y', 'ACC_Z')
            except FileNotFoundError:
                pass
            except UnicodeDecodeError:
                pass

    def __load_data(self):
        if self.__shared_data.data_file_path is not None:
            self.__shared_data.import_parameter()

    def __show_data(self, field1, field2, field3):
        if self.__shared_data.parameter is not None:
            # Generate the timecodes if needed
            if len(self.__shared_data.parameter['TIMECODE']) == 0:
                if self.__shared_data.sampling_rate is None:
                    result = False
                    while not result and result == 0:
                        result = self.__show_sampling_rate_picker()

                self.__shared_data.add_timecode()

            self.__x_axis_viewbox.clear()
            self.__y_axis_viewbox.clear()
            self.__z_axis_viewbox.clear()

            # Show the 3 selected fields
            self.__x_axis_viewbox.addItem(PlotCurveItem(list(map(int, self.__shared_data.parameter.get(field1))),
                                                        pen='#34495e'))
            self.__y_axis_viewbox.addItem(PlotCurveItem(list(map(int, self.__shared_data.parameter.get(field2))),
                                                        pen='#9b59b6'))
            self.__z_axis_viewbox.addItem(PlotCurveItem(list(map(int, self.__shared_data.parameter.get(field3))),
                                                        pen='#3498db'))

            self.__x_axis_item.setLabel(field1, color="#34495e")
            self.__y_axis_item.setLabel(field2, color="#9b59b6")
            self.__z_axis_item.setLabel(field3, color="#3498db")

            # Add the middle line and the bottom timecodes
            timecodes = self.__shared_data.parameter['TIMECODE']
            middle = [0] * len(timecodes)
            self.__plot_item_viewbox.addItem(PlotCurveItem(middle, pen='#000000'))
            self.__plot_item.getAxis('bottom').setTicks(
                self.__generate_time_ticks(timecodes, self.__shared_data.sampling_rate))

            # Enable the controls
            self.__sync_time_edit.setEnabled(True)
            self.__sync_time_button.setEnabled(True)
            self.__dir_picker_button.setEnabled(False)
        self.__update_views()

    def __update_views(self):
        self.__x_axis_viewbox.setGeometry(self.__plot_item_viewbox.sceneBoundingRect())
        self.__y_axis_viewbox.setGeometry(self.__plot_item_viewbox.sceneBoundingRect())
        self.__z_axis_viewbox.setGeometry(self.__plot_item_viewbox.sceneBoundingRect())

    def __generate_time_ticks(self, timecodes, rate):
        ticks = list()

        steps = [rate * 30, rate * 15, rate * 5, rate]
        for step in steps:
            temp = list()
            i = step
            while i in range(len(timecodes)):
                temp.append((i, timecodes[i].strftime('%H:%M:%S:') + str(int(timecodes[i].microsecond / 1000))))
                i += step
            ticks.append(temp)

        return ticks

    def __sync_data(self):
        self.__shared_data.data_sync = self.__sync_time_edit.text()
        self.__shared_data.update_sync.emit()

    def __show_sampling_rate_picker(self) -> bool:
        self.__shared_data.sampling_rate, result = QInputDialog.getInt(self, 'Set sampling rate value', 'Sampling rate')
        return result

    def __add_selector_acc_gyr(self):
        if 'GYR_X' in self.__shared_data.parameter.keys() or 'GYR_Y' in self.__shared_data.parameter.keys() \
                or 'GYR_Z' in self.__shared_data.parameter.keys():
            show_acc = QPushButton()
            show_acc.setText('Show Accelerometer Axis')
            show_acc.clicked.connect(self.__show_acc)

            show_gyr = QPushButton()
            show_gyr.setText('Show Gyroscope Axis')
            show_gyr.clicked.connect(self.__show_gyr)

            layout = QHBoxLayout()
            layout.addWidget(show_acc)
            layout.addWidget(show_gyr)
            layout.addStretch(1)

            self.__v_box.addLayout(layout)

    def __show_acc(self):
        self.__show_data('ACC_X', 'ACC_Y', 'ACC_Z')

    def __show_gyr(self):
        self.__show_data('GYR_X', 'GYR_Y', 'GYR_Z')

    def __restore_state(self):
        if self.__shared_data.parameter is not None:
            self.__add_selector_acc_gyr()
            self.__show_data('ACC_X', 'ACC_Y', 'ACC_Z')
            print('trigger reimport')
        if self.__shared_data.data_sync is not None:
            text_time = self.__shared_data.data_sync.split(':')
            time = QTime()
            time.setHMS(int(text_time[0]), int(text_time[1]), int(text_time[2]), int(text_time[3]))
            self.__sync_time_edit.setTime(time)