Exemplo n.º 1
0
class MainWindow(QMainWindow, Ui_MainWindow):

    """Main window of the program."""

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.nc_editor = NCEditor(self)
        self.nc_code_layout.addWidget(self.nc_editor)
        h_size = 15
        self.parameter_table = MathTableWidget([
            r"$a$",
            r"$b$",
            r"$\zeta$",
            r"$\omega_n$",
        ], h_size, self)
        self.parameter_table.verticalHeader().hide()
        self.parameter_table.setRowCount(1)
        self.parameter_table.setFixedHeight(h_size * 4)
        self.parameter_table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.parameter_table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        for i, v in enumerate([1., 20., 0.707, 1000.]):
            self.parameter_table.setCellWidget(0, i, _spinbox(v))
        self.nc_code_layout.insertWidget(1, self.parameter_table)

        self.env = ""
        self.file_name = ""
        self.set_locate(QStandardPaths.writableLocation(QStandardPaths.DesktopLocation))

        # Default RE compiler.
        self.re_compiler.setPlaceholderText(DEFAULT_NC_SYNTAX)

        # Chart widgets.
        self.charts = [QChart() for _ in range(8)]
        for chart, layout in zip(self.charts, [self.s_layout, self.v_layout, self.a_layout, self.j_layout] * 2):
            chart.setTheme(QChart.ChartThemeLight)
            view = QChartView(chart)
            view.setContextMenuPolicy(Qt.CustomContextMenu)
            view.customContextMenuRequested.connect(self.__save_chart_func(view))
            view.setRenderHint(QPainter.Antialiasing)
            layout.addWidget(view)

        # Chart menu
        self.chart_menu = QMenu(self)
        self.save_chart_action = QAction("Save as image", self)
        self.chart_menu.addAction(self.save_chart_action)
        self.copy_chart_action = QAction("Copy as pixmap", self)
        self.chart_menu.addAction(self.copy_chart_action)

        # Splitter
        self.main_splitter.setSizes([300, 500])

    def output_to(self, format_name: str, format_choose: Sequence[str]) -> str:
        """Simple to support multiple format."""
        file_name, suffix = QFileDialog.getSaveFileName(
            self,
            f"Save to {format_name}...",
            self.env + '/Untitled',
            ';;'.join(format_choose)
        )
        if file_name:
            suffix = str_between(suffix, '(', ')').split('*')[-1]
            print(f"Format: {suffix}")
            if QFileInfo(file_name).completeSuffix() != suffix[1:]:
                file_name += suffix
        return file_name

    def input_from(self, format_name: str, format_choose: Sequence[str]) -> str:
        """Get file name(s)."""
        file_name_s, suffix = QFileDialog.getOpenFileName(
            self,
            f"Open {format_name} file...",
            self.env,
            ';;'.join(format_choose)
        )
        if file_name_s:
            suffix = str_between(suffix, '(', ')').split('*')[-1]
            print(f"Format: {suffix}")
            self.set_locate(QFileInfo(file_name_s).absolutePath())
            self.set_file_name(file_name_s)
        return file_name_s

    def set_locate(self, locate: str):
        """Set environment variables."""
        if locate == self.env:
            # If no changed.
            return
        self.env = locate
        print(f"~Set workplace to: [\"{self.env}\"]")

    def set_file_name(self, new_name: str):
        """Set default file name."""
        self.file_name = new_name

    def dragEnterEvent(self, event):
        """Drag file in to our window."""
        mime_data = event.mimeData()
        if not mime_data.hasUrls():
            return
        for url in mime_data.urls():
            suffix = QFileInfo(url.toLocalFile()).completeSuffix()
            if suffix in {'nc', 'g'}:
                event.acceptProposedAction()

    def dropEvent(self, event):
        """Drop file in to our window."""
        file_name = event.mimeData().urls()[-1].toLocalFile()
        self.__load_nc_code(file_name)
        event.acceptProposedAction()

    @pyqtSlot(name='on_nc_load_button_clicked')
    def __load_nc_code(self, file_name: str = ""):
        if not file_name:
            file_name = self.input_from("NC code", [
                "Numerical Control programming language (*.nc)",
                "Preparatory code (*.g)",
            ])
            if not file_name:
                return

        self.nc_file_path.setText(file_name)
        with open(file_name, 'r', encoding='utf-8') as f:
            self.nc_editor.setText(f.read())

    @pyqtSlot(name='on_nc_save_button_clicked')
    def __save_nc_code(self):
        """Save current NC code."""
        if self.nc_file_path.text() != self.file_name or not self.file_name:
            file_name = self.output_to("NC code", [
                "Numerical control programming language (*.nc)",
                "Preparatory code (*.g)",
            ])
            if not file_name:
                return

            self.file_name = file_name

        with open(self.file_name, 'w', encoding='utf-8') as f:
            f.write(self.nc_editor.text())

    def __save_chart_func(self, chart: QChartView) -> Callable[[QPoint], None]:
        """Save chart context menu."""

        @pyqtSlot(QPoint)
        def save_chart(pos: QPoint):
            self.chart_menu.popup(chart.mapToGlobal(pos))
            action = self.chart_menu.exec()
            if action not in {self.save_chart_action, self.copy_chart_action}:
                return

            pixmap: QPixmap = chart.grab()
            if action == self.copy_chart_action:
                QApplication.clipboard().setPixmap(pixmap)
                return

            file_name = self.output_to("Image", qt_image_format)
            if not file_name:
                return

            pixmap.save(file_name)

        return save_chart

    @pyqtSlot(name='on_nc_compile_clicked')
    def __nc_compile(self):
        """Compile NC code."""
        self.__clear_charts()

        lines = []
        for name in [
            "Position",
            "Velocity",
            "Accelerate",
            "Jerk",
            "Original Position",
            "Velocity of X",
            "Velocity of Y",
            "Accelerate of X",
            "Accelerate of Y",
            "Jerk of X",
            "Jerk of Y",
            "Simulated Position",
        ]:
            line = QLineSeries()
            line.setName(name)
            lines.append(line)

        syntax = self.re_compiler.text() or self.re_compiler.placeholderText()
        if self.trapezoid_option.isChecked():
            strategy = Trapezoid
        else:
            strategy = SShape

        i = 0.
        sx_plot = []
        sy_plot = []
        ts = None
        for tp in graph_chart(self.nc_editor.text(), syntax, strategy):
            for s, v, a, j, (sx, sy), (vx, vy), (ax, ay), (jx, jy) in tp.iter(
                tp.s,
                tp.v,
                tp.a,
                tp.j,
                tp.s_xy,
                tp.v_xy,
                tp.a_xy,
                tp.j_xy,
            ):
                lines[0].append(i, s)
                lines[1].append(i, v)
                lines[2].append(i, a)
                lines[3].append(i, j)
                lines[4].append(sx, sy)
                lines[5].append(i, vx)
                lines[6].append(i, vy)
                lines[7].append(i, ax)
                lines[8].append(i, ay)
                lines[9].append(i, jx)
                lines[10].append(i, jy)
                sx_plot.append(sx)
                sy_plot.append(sy)
                i += tp.t_s
                if ts is None:
                    ts = tp.t_s

        for ssx, ssy in zip(*self.__simulation_output(sx_plot, sy_plot, ts or 1e-3)):
            lines[-1].append(ssx, ssy)

        for line, chart in zip(lines, [
            self.charts[0],
            self.charts[1],
            self.charts[2],
            self.charts[3],
            self.charts[4],
            self.charts[5],
            self.charts[5],
            self.charts[6],
            self.charts[6],
            self.charts[7],
            self.charts[7],
            self.charts[4],
        ]):
            chart.addSeries(line)
        self.__reset_axis()

    def __simulation_output(self, sx_plot, sy_plot, t_s):
        parameter = []
        for c in range(self.parameter_table.columnCount()):
            parameter.append(self.parameter_table.cellWidget(0, c).value())
        num, den = model_system(*parameter)
        _, xout = control_num_den(num, den, t_s, sx_plot)
        _, yout = control_num_den(num, den, t_s, sy_plot)
        return xout, yout

    def __clear_charts(self):
        """Clear all charts."""
        for chart in self.charts:
            chart.removeAllSeries()
            for axis in chart.axes():
                chart.removeAxis(axis)

    def __reset_axis(self):
        """Reset all axis of charts."""
        units = [" (mm)", " (mm/s)", " (mm/s^2)", " (mm/s^3)"]
        vaj = ["Velocity", "Accelerate", "Jerk"]
        for chart, x_axis, y_axis in zip(
            self.charts,
            ["Time (s)"] * 4 + ["X (mm)"] + ["Time (s)"] * 3,
            map(lambda y, u: y + u, ["Position"] + vaj + ["Y"] + vaj, units * 2),
        ):
            chart.createDefaultAxes()
            chart.axisX().setTitleText(x_axis)
            chart.axisY().setTitleText(y_axis)
Exemplo n.º 2
0
class InputsWidget(QWidget, Ui_Form):

    """There has following functions:

    + Function of mechanism variables settings.
    + Path recording.
    """

    about_to_resolve = Signal()

    def __init__(self, parent: MainWindowBase):
        super(InputsWidget, self).__init__(parent)
        self.setupUi(self)

        # parent's function pointer.
        self.free_move_button = parent.free_move_button
        self.EntitiesPoint = parent.entities_point
        self.EntitiesLink = parent.entities_link
        self.MainCanvas = parent.main_canvas
        self.solve = parent.solve
        self.reload_canvas = parent.reload_canvas
        self.output_to = parent.output_to
        self.conflict = parent.conflict
        self.dof = parent.dof
        self.right_input = parent.right_input
        self.CommandStack = parent.command_stack
        self.set_coords_as_current = parent.set_coords_as_current

        # Angle panel
        self.dial = QDial()
        self.dial.setStatusTip("Input widget of rotatable joint.")
        self.dial.setEnabled(False)
        self.dial.valueChanged.connect(self.__update_var)
        self.dial_spinbox.valueChanged.connect(self.__set_var)
        self.inputs_dial_layout.addWidget(RotatableView(self.dial))

        # Angle panel available check
        self.variable_list.currentRowChanged.connect(self.__dial_ok)

        # Play button.
        action = QShortcut(QKeySequence("F5"), self)
        action.activated.connect(self.variable_play.click)
        self.variable_stop.clicked.connect(self.variable_value_reset)

        # Timer for play button.
        self.inputs_play_shaft = QTimer()
        self.inputs_play_shaft.setInterval(10)
        self.inputs_play_shaft.timeout.connect(self.__change_index)

        # Change the point coordinates with current position.
        self.update_pos.clicked.connect(self.set_coords_as_current)

        # Inputs record context menu
        self.pop_menu_record_list = QMenu(self)
        self.record_list.customContextMenuRequested.connect(
            self.__record_list_context_menu
        )
        self.__path_data: Dict[str, Sequence[_Coord]] = {}

    def clear(self):
        """Clear function to reset widget status."""
        self.__path_data.clear()
        for _ in range(self.record_list.count() - 1):
            self.record_list.takeItem(1)
        self.variable_list.clear()

    def __set_angle_mode(self):
        """Change to angle input."""
        self.dial.setMinimum(0)
        self.dial.setMaximum(36000)
        self.dial_spinbox.setMinimum(0)
        self.dial_spinbox.setMaximum(360)

    def __set_unit_mode(self):
        """Change to unit input."""
        self.dial.setMinimum(-50000)
        self.dial.setMaximum(50000)
        self.dial_spinbox.setMinimum(-500)
        self.dial_spinbox.setMaximum(500)

    def path_data(self):
        """Return current path data."""
        return self.__path_data

    @Slot(tuple)
    def set_selection(self, selections: Sequence[int]):
        """Set one selection from canvas."""
        self.joint_list.setCurrentRow(selections[0])

    @Slot()
    def clear_selection(self):
        """Clear the points selection."""
        self.driver_list.clear()
        self.joint_list.setCurrentRow(-1)

    @Slot(int, name='on_joint_list_currentRowChanged')
    def __update_relate_points(self, _: int):
        """Change the point row from input widget."""
        self.driver_list.clear()

        item: Optional[QListWidgetItem] = self.joint_list.currentItem()
        if item is None:
            return
        p0 = _variable_int(item.text())

        vpoints = self.EntitiesPoint.data_tuple()
        type_int = vpoints[p0].type
        if type_int == VJoint.R:
            for i, vpoint in enumerate(vpoints):
                if i == p0:
                    continue
                if vpoints[p0].same_link(vpoint):
                    if vpoints[p0].grounded() and vpoint.grounded():
                        continue
                    self.driver_list.addItem(f"[{vpoint.type_str}] Point{i}")
        elif type_int in {VJoint.P, VJoint.RP}:
            self.driver_list.addItem(f"[{vpoints[p0].type_str}] Point{p0}")

    @Slot(int, name='on_driver_list_currentRowChanged')
    def __set_add_var_enabled(self, _: int):
        """Set enable of 'add variable' button."""
        driver = self.driver_list.currentIndex()
        self.variable_add.setEnabled(driver != -1)

    @Slot(name='on_variable_add_clicked')
    def __add_inputs_variable(self, p0: Optional[int] = None, p1: Optional[int] = None):
        """Add variable with '->' sign."""
        if p0 is None:
            item: Optional[QListWidgetItem] = self.joint_list.currentItem()
            if item is None:
                return
            p0 = _variable_int(item.text())
        if p1 is None:
            item: Optional[QListWidgetItem] = self.driver_list.currentItem()
            if item is None:
                return
            p1 = _variable_int(item.text())

        # Check DOF.
        if self.dof() <= self.input_count():
            QMessageBox.warning(
                self,
                "Wrong DOF",
                "The number of variable must no more than degrees of freedom."
            )
            return

        # Check same link.
        vpoints = self.EntitiesPoint.data_tuple()
        if not vpoints[p0].same_link(vpoints[p1]):
            QMessageBox.warning(
                self,
                "Wrong pair",
                "The base point and driver point should at the same link."
            )
            return

        # Check repeated pairs.
        for p0_, p1_, a in self.input_pairs():
            if {p0, p1} == {p0_, p1_} and vpoints[p0].type == VJoint.R:
                QMessageBox.warning(
                    self,
                    "Wrong pair",
                    "There already have a same pair."
                )
                return

        name = f'Point{p0}'
        self.CommandStack.beginMacro(f"Add variable of {name}")
        if p0 == p1:
            # One joint by offset.
            value = vpoints[p0].true_offset()
        else:
            # Two joints by angle.
            value = vpoints[p0].slope_angle(vpoints[p1])
        self.CommandStack.push(AddVariable('->'.join((
            name,
            f'Point{p1}',
            f"{value:.02f}",
        )), self.variable_list))
        self.CommandStack.endMacro()

    def add_inputs_variables(self, variables: Sequence[Tuple[int, int]]):
        """Add from database."""
        for p0, p1 in variables:
            self.__add_inputs_variable(p0, p1)

    @Slot()
    def __dial_ok(self):
        """Set the angle of base link and drive link."""
        row = self.variable_list.currentRow()
        enabled = row > -1
        rotatable = (
            enabled and
            not self.free_move_button.isChecked() and
            self.right_input()
        )
        self.dial.setEnabled(rotatable)
        self.dial_spinbox.setEnabled(rotatable)
        self.oldVar = self.dial.value() / 100.
        self.variable_play.setEnabled(rotatable)
        self.variable_speed.setEnabled(rotatable)
        item: Optional[QListWidgetItem] = self.variable_list.currentItem()
        if item is None:
            return
        expr = item.text().split('->')
        p0 = int(expr[0].replace('Point', ''))
        p1 = int(expr[1].replace('Point', ''))
        value = float(expr[2])
        if p0 == p1:
            self.__set_unit_mode()
        else:
            self.__set_angle_mode()
        self.dial.setValue(value * 100 if enabled else 0)

    def variable_excluding(self, row: Optional[int] = None):
        """Remove variable if the point was been deleted. Default: all."""
        one_row: bool = row is not None
        for i, (b, d, a) in enumerate(self.input_pairs()):
            # If this is not origin point any more.
            if one_row and (row != b):
                continue
            self.CommandStack.beginMacro(f"Remove variable of Point{row}")
            self.CommandStack.push(DeleteVariable(i, self.variable_list))
            self.CommandStack.endMacro()

    @Slot(name='on_variable_remove_clicked')
    def remove_var(self, row: int = -1):
        """Remove and reset angle."""
        if row == -1:
            row = self.variable_list.currentRow()
        if not row > -1:
            return
        self.variable_stop.click()
        self.CommandStack.beginMacro(f"Remove variable of Point{row}")
        self.CommandStack.push(DeleteVariable(row, self.variable_list))
        self.CommandStack.endMacro()
        self.EntitiesPoint.get_back_position()
        self.solve()

    def interval(self) -> float:
        """Return interval value."""
        return self.record_interval.value()

    def input_count(self) -> int:
        """Use to show input variable count."""
        return self.variable_list.count()

    def input_pairs(self) -> Iterator[Tuple[int, int, float]]:
        """Back as point number code."""
        for row in range(self.variable_list.count()):
            var = self.variable_list.item(row).text().split('->')
            p0 = int(var[0].replace('Point', ''))
            p1 = int(var[1].replace('Point', ''))
            angle = float(var[2])
            yield (p0, p1, angle)

    def variable_reload(self):
        """Auto check the points and type."""
        self.joint_list.clear()
        for i in range(self.EntitiesPoint.rowCount()):
            type_text = self.EntitiesPoint.item(i, 2).text()
            self.joint_list.addItem(f"[{type_text}] Point{i}")
        self.variable_value_reset()

    @Slot(float)
    def __set_var(self, value: float):
        self.dial.setValue(int(value * 100 % self.dial.maximum()))

    @Slot(int)
    def __update_var(self, value: int):
        """Update the value when rotating QDial."""
        item = self.variable_list.currentItem()
        value /= 100.
        self.dial_spinbox.blockSignals(True)
        self.dial_spinbox.setValue(value)
        self.dial_spinbox.blockSignals(False)
        if item:
            item_text = item.text().split('->')
            item_text[-1] = f"{value:.02f}"
            item.setText('->'.join(item_text))
            self.about_to_resolve.emit()
        if (
            self.record_start.isChecked() and
            abs(self.oldVar - value) > self.record_interval.value()
        ):
            self.MainCanvas.record_path()
            self.oldVar = value

    def variable_value_reset(self):
        """Reset the value of QDial."""
        if self.inputs_play_shaft.isActive():
            self.variable_play.setChecked(False)
            self.inputs_play_shaft.stop()
        self.EntitiesPoint.get_back_position()
        vpoints = self.EntitiesPoint.data_tuple()
        for i, (p0, p1, a) in enumerate(self.input_pairs()):
            self.variable_list.item(i).setText('->'.join([
                f'Point{p0}',
                f'Point{p1}',
                f"{vpoints[p0].slope_angle(vpoints[p1]):.02f}",
            ]))
        self.__dial_ok()
        self.solve()

    @Slot(bool, name='on_variable_play_toggled')
    def __play(self, toggled: bool):
        """Triggered when play button was changed."""
        self.dial.setEnabled(not toggled)
        self.dial_spinbox.setEnabled(not toggled)
        if toggled:
            self.inputs_play_shaft.start()
        else:
            self.inputs_play_shaft.stop()
            if self.update_pos_option.isChecked():
                self.set_coords_as_current()

    @Slot()
    def __change_index(self):
        """QTimer change index."""
        index = self.dial.value()
        speed = self.variable_speed.value()
        extreme_rebound = (
            self.conflict.isVisible() and
            self.extremeRebound.isChecked()
        )
        if extreme_rebound:
            speed = -speed
            self.variable_speed.setValue(speed)
        index += int(speed * 6 * (3 if extreme_rebound else 1))
        index %= self.dial.maximum()
        self.dial.setValue(index)

    @Slot(bool, name='on_record_start_toggled')
    def __start_record(self, toggled: bool):
        """Save to file path data."""
        if toggled:
            self.MainCanvas.record_start(int(
                self.dial_spinbox.maximum() / self.record_interval.value()
            ))
            return
        path = self.MainCanvas.get_record_path()
        name, ok = QInputDialog.getText(
            self,
            "Recording completed!",
            "Please input name tag:"
        )
        i = 0
        name = name or f"Record_{i}"
        while name in self.__path_data:
            name = f"Record_{i}"
            i += 1
        QMessageBox.information(
            self,
            "Record",
            "The name tag is being used or empty."
        )
        self.add_path(name, path)

    def add_path(self, name: str, path: Sequence[_Coord]):
        """Add path function."""
        self.CommandStack.beginMacro(f"Add {{Path: {name}}}")
        self.CommandStack.push(AddPath(
            self.record_list,
            name,
            self.__path_data,
            path
        ))
        self.CommandStack.endMacro()
        self.record_list.setCurrentRow(self.record_list.count() - 1)

    def load_paths(self, paths: Dict[str, Sequence[_Coord]]):
        """Add multiple path."""
        for name, path in paths.items():
            self.add_path(name, path)

    @Slot(name='on_record_remove_clicked')
    def __remove_path(self):
        """Remove path data."""
        row = self.record_list.currentRow()
        if not row > 0:
            return
        name = self.record_list.item(row).text()
        self.CommandStack.beginMacro(f"Delete {{Path: {name}}}")
        self.CommandStack.push(DeletePath(
            row,
            self.record_list,
            self.__path_data
        ))
        self.CommandStack.endMacro()
        self.record_list.setCurrentRow(self.record_list.count() - 1)
        self.reload_canvas()

    @Slot(QListWidgetItem, name='on_record_list_itemDoubleClicked')
    def __path_dlg(self, item: QListWidgetItem):
        """View path data."""
        name = item.text().split(":")[0]
        try:
            data = self.__path_data[name]
        except KeyError:
            return

        points_text = ", ".join(f"Point{i}" for i in range(len(data)))
        if QMessageBox.question(
            self,
            "Path data",
            f"This path data including {points_text}.",
            (QMessageBox.Save | QMessageBox.Close),
            QMessageBox.Close
        ) != QMessageBox.Save:
            return

        file_name = self.output_to(
            "path data",
            ["Comma-Separated Values (*.csv)", "Text file (*.txt)"]
        )
        if not file_name:
            return

        with open(file_name, 'w', encoding='utf-8', newline='') as stream:
            writer = csv.writer(stream)
            for point in data:
                for coordinate in point:
                    writer.writerow(coordinate)
                writer.writerow(())
        logger.info(f"Output path data: {file_name}")

    @Slot(QPoint)
    def __record_list_context_menu(self, point):
        """Show the context menu.

        Show path [0], [1], ...
        Or copy path coordinates.
        """
        row = self.record_list.currentRow()
        if not row > -1:
            return
        showall_action = self.pop_menu_record_list.addAction("Show all")
        showall_action.index = -1
        copy_action = self.pop_menu_record_list.addAction("Copy as new")
        name = self.record_list.item(row).text().split(':')[0]
        try:
            data = self.__path_data[name]
        except KeyError:
            # Auto preview path.
            data = self.MainCanvas.Path.path
            showall_action.setEnabled(False)
        else:
            for action_text in ("Show", "Copy data from"):
                self.pop_menu_record_list.addSeparator()
                for i in range(len(data)):
                    if data[i]:
                        action = self.pop_menu_record_list.addAction(
                            f"{action_text} Point{i}"
                        )
                        action.index = i
        action_exec = self.pop_menu_record_list.exec(
            self.record_list.mapToGlobal(point)
        )
        if action_exec:
            if action_exec == copy_action:
                # Copy path data.
                num = 0
                name_copy = f"{name}_{num}"
                while name_copy in self.__path_data:
                    name_copy = f"{name}_{num}"
                    num += 1
                self.add_path(name_copy, data)
            elif "Copy data from" in action_exec.text():
                # Copy data to clipboard.
                QApplication.clipboard().setText('\n'.join(
                    f"{x},{y}" for x, y in data[action_exec.index]
                ))
            elif "Show" in action_exec.text():
                # Switch points enabled status.
                if action_exec.index == -1:
                    self.record_show.setChecked(True)
                self.MainCanvas.set_path_show(action_exec.index)
        self.pop_menu_record_list.clear()

    @Slot(bool, name='on_record_show_toggled')
    def __set_path_show(self, toggled: bool):
        """Show all paths or hide."""
        self.MainCanvas.set_path_show(-1 if toggled else -2)

    @Slot(int, name='on_record_list_currentRowChanged')
    def __set_path(self, _: int):
        """Reload the canvas when switch the path."""
        if not self.record_show.isChecked():
            self.record_show.setChecked(True)
        self.reload_canvas()

    def current_path(self):
        """Return current path data to main canvas.

        + No path.
        + Show path data.
        + Auto preview.
        """
        row = self.record_list.currentRow()
        if row in {0, -1}:
            return ()
        path_name = self.record_list.item(row).text().split(':')[0]
        return self.__path_data.get(path_name, ())

    @Slot(name='on_variable_up_clicked')
    @Slot(name='on_variable_down_clicked')
    def __set_variable_priority(self):
        row = self.variable_list.currentRow()
        if not row > -1:
            return
        item = self.variable_list.currentItem()
        self.variable_list.insertItem(
            row + (-1 if self.sender() == self.variable_up else 1),
            self.variable_list.takeItem(row)
        )
        self.variable_list.setCurrentItem(item)