383 lines
14 KiB
Python
383 lines
14 KiB
Python
|
|
from PySide6.QtCore import Qt, Signal
|
|||
|
|
from PySide6.QtWidgets import (
|
|||
|
|
QApplication,
|
|||
|
|
QWidget,
|
|||
|
|
QVBoxLayout,
|
|||
|
|
QHBoxLayout,
|
|||
|
|
QFormLayout,
|
|||
|
|
QGroupBox,
|
|||
|
|
QDialog,
|
|||
|
|
)
|
|||
|
|
from qfluentwidgets import (
|
|||
|
|
PushButton,
|
|||
|
|
ComboBox,
|
|||
|
|
DoubleSpinBox,
|
|||
|
|
SpinBox,
|
|||
|
|
setTheme,
|
|||
|
|
Theme,
|
|||
|
|
StrongBodyLabel,
|
|||
|
|
EditableComboBox,
|
|||
|
|
RadioButton,
|
|||
|
|
isDarkTheme,
|
|||
|
|
MessageBox,
|
|||
|
|
)
|
|||
|
|
from ...model.point_state import PointState
|
|||
|
|
|
|||
|
|
|
|||
|
|
class StatusEditDialog(QDialog):
|
|||
|
|
point_state_applied = Signal(int, PointState)
|
|||
|
|
|
|||
|
|
def __init__(self, pos_name, selected_row_idx, parent=None):
|
|||
|
|
super().__init__(parent)
|
|||
|
|
# 窗口基本设置
|
|||
|
|
self.setWindowTitle("状态编辑") # 设置窗口标题
|
|||
|
|
self.resize(600, 660) # 窗口大小
|
|||
|
|
self.setMinimumSize(550, 560) # 最小尺寸限制
|
|||
|
|
|
|||
|
|
# 点位名称
|
|||
|
|
self.pos_name = pos_name
|
|||
|
|
|
|||
|
|
# 选中行的行索引
|
|||
|
|
self.selected_row_idx = selected_row_idx
|
|||
|
|
|
|||
|
|
self.__initWidget()
|
|||
|
|
|
|||
|
|
def __initWidget(self):
|
|||
|
|
# 创建控件
|
|||
|
|
self.__createWidget()
|
|||
|
|
|
|||
|
|
# 设置样式
|
|||
|
|
self.__initStyles()
|
|||
|
|
|
|||
|
|
# 设置布局
|
|||
|
|
self.__initLayout()
|
|||
|
|
|
|||
|
|
# 绑定
|
|||
|
|
self.__bind()
|
|||
|
|
|
|||
|
|
# 创建相关控件
|
|||
|
|
def __createWidget(self):
|
|||
|
|
# 1. 点位名称输入
|
|||
|
|
self.name_combo = EditableComboBox()
|
|||
|
|
|
|||
|
|
self.name_combo.addItems(
|
|||
|
|
["抓取点", "破袋点", "震动点", "扔袋点", "相机/待抓点"]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 检查点位名称在下拉框是否已经存在
|
|||
|
|
target_pos_name = self.pos_name
|
|||
|
|
pos_name_index = self.name_combo.findText(target_pos_name)
|
|||
|
|
# 若未找到(索引=-1),则添加 表单中 的点位名字
|
|||
|
|
if pos_name_index == -1:
|
|||
|
|
self.name_combo.addItem(self.pos_name)
|
|||
|
|
# 选中新添加的这项
|
|||
|
|
new_index = self.name_combo.count() - 1
|
|||
|
|
self.name_combo.setCurrentIndex(new_index)
|
|||
|
|
else:
|
|||
|
|
# 已经存在的话就直接选中
|
|||
|
|
self.name_combo.setCurrentIndex(pos_name_index)
|
|||
|
|
|
|||
|
|
self.name_combo.setPlaceholderText("请设置点位名称")
|
|||
|
|
|
|||
|
|
# 2. 工具坐标系id
|
|||
|
|
self.tool_coord_spin = SpinBox()
|
|||
|
|
self.tool_coord_spin.setRange(0, 99) # 0-99范围
|
|||
|
|
self.tool_coord_spin.setValue(0) # 默认值
|
|||
|
|
self.tool_coord_btn = PushButton("获取当前工具坐标id")
|
|||
|
|
|
|||
|
|
# 3. 工件坐标系id
|
|||
|
|
self.work_coord_spin = SpinBox()
|
|||
|
|
self.work_coord_spin.setRange(0, 99) # 0-99范围
|
|||
|
|
self.work_coord_spin.setValue(0) # 默认值
|
|||
|
|
self.work_coord_btn = PushButton("获取当前工件坐标id")
|
|||
|
|
|
|||
|
|
# 4-9. 关节坐标 J1 到 J6
|
|||
|
|
self.j_spins = []
|
|||
|
|
for _ in range(6):
|
|||
|
|
spin = DoubleSpinBox()
|
|||
|
|
spin.setRange(-180, 180) # 角度范围 (-180度到180度)
|
|||
|
|
spin.setDecimals(3) # 保留3位小数
|
|||
|
|
spin.setSingleStep(0.001) # 默认步长
|
|||
|
|
self.j_spins.append(spin)
|
|||
|
|
|
|||
|
|
# 关节坐标默认值 (默认为无效值)
|
|||
|
|
self.j_spins[0].setValue(-9999)
|
|||
|
|
self.j_spins[1].setValue(-9999)
|
|||
|
|
self.j_spins[2].setValue(-9999)
|
|||
|
|
self.j_spins[3].setValue(-9999)
|
|||
|
|
self.j_spins[4].setValue(-9999)
|
|||
|
|
self.j_spins[5].setValue(-9999)
|
|||
|
|
|
|||
|
|
# 关节坐标设置 右侧的步长设置 和 获取关节坐标按钮
|
|||
|
|
self.step_group = QGroupBox("单击步长设置")
|
|||
|
|
self.step_input = DoubleSpinBox()
|
|||
|
|
self.step_input.setRange(0.001, 180.0) # 步长范围
|
|||
|
|
self.step_input.setDecimals(3) # 保留3位小数
|
|||
|
|
self.step_input.setValue(0.001) # 默认步长
|
|||
|
|
self.get_values_btn = PushButton("获取当前J1-J6值")
|
|||
|
|
|
|||
|
|
# 10. 速度 (移动速度)
|
|||
|
|
self.approach_speed_spin = DoubleSpinBox()
|
|||
|
|
self.approach_speed_spin.setRange(0, 100)
|
|||
|
|
self.approach_speed_spin.setDecimals(0) # 小数点
|
|||
|
|
self.approach_speed_spin.setValue(20)
|
|||
|
|
self.approach_speed_spin.setSingleStep(10)
|
|||
|
|
|
|||
|
|
# 11. 运动类型(下拉选择)
|
|||
|
|
self.motion_type_combo = ComboBox()
|
|||
|
|
self.motion_type_combo.addItems(["直线", "曲线中间点", "曲线终点", "自由路径"])
|
|||
|
|
|
|||
|
|
# 12. 平滑选择
|
|||
|
|
self.stop_radio = RadioButton("停止")
|
|||
|
|
self.smooth_radio = RadioButton("平滑过渡")
|
|||
|
|
self.smooth_ms_spin = DoubleSpinBox() # 平滑过渡的时间(毫秒)
|
|||
|
|
self.smooth_ms_spin.setRange(0, 500) # 范围:0 - 500 ms
|
|||
|
|
self.smooth_ms_spin.setDecimals(0) # 整数毫秒
|
|||
|
|
self.smooth_ms_spin.setValue(0) # 默认值0
|
|||
|
|
self.smooth_ms_spin.setSingleStep(10) # 步长:10毫秒
|
|||
|
|
self.smooth_ms_spin.setEnabled(False) # 初始禁用(仅“平滑过渡”选中时启用)
|
|||
|
|
self.stop_radio.setChecked(True) # 默认选“停止”
|
|||
|
|
|
|||
|
|
# 13. 应用按钮
|
|||
|
|
self.apply_btn = PushButton("应用")
|
|||
|
|
self.apply_btn.setMinimumWidth(160) # 按钮最小宽度
|
|||
|
|
|
|||
|
|
def __initStyles(self):
|
|||
|
|
# 根据主题设置样式表
|
|||
|
|
if isDarkTheme(): # 深色主题
|
|||
|
|
self.step_group.setStyleSheet(
|
|||
|
|
"""
|
|||
|
|
QGroupBox {
|
|||
|
|
color: white; /* 标题文字颜色 */
|
|||
|
|
border: 1px solid white; /* 边框线条颜色和宽度 */
|
|||
|
|
border-radius: 6px; /* 边框圆角 */
|
|||
|
|
margin-top: 10px; /* 标题与边框的距离 */
|
|||
|
|
}
|
|||
|
|
QGroupBox::title {
|
|||
|
|
subcontrol-origin: margin;
|
|||
|
|
subcontrol-position: top left; /* 标题位置 */
|
|||
|
|
left: 10px; /* 标题左边距 */
|
|||
|
|
padding: 0 3px 0 3px; /* 标题内边距 */
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
)
|
|||
|
|
self.setStyleSheet("background-color: rgb(32, 32, 32);")
|
|||
|
|
else: # 浅色主题
|
|||
|
|
self.step_group.setStyleSheet(
|
|||
|
|
"""
|
|||
|
|
QGroupBox {
|
|||
|
|
color: black; /* 标题文字颜色 */
|
|||
|
|
border: 1px solid black; /* 边框线条颜色和宽度 */
|
|||
|
|
border-radius: 6px; /* 边框圆角 */
|
|||
|
|
margin-top: 10px; /* 标题与边框的距离 */
|
|||
|
|
}
|
|||
|
|
QGroupBox::title {
|
|||
|
|
subcontrol-origin: margin;
|
|||
|
|
subcontrol-position: top left; /* 标题位置 */
|
|||
|
|
left: 10px; /* 标题左边距 */
|
|||
|
|
padding: 0 3px 0 3px; /* 标题内边距 */
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
)
|
|||
|
|
self.setStyleSheet("background-color: rgb(243, 243, 243);")
|
|||
|
|
|
|||
|
|
def __initLayout(self):
|
|||
|
|
# 主布局(直接应用于当前Widget)
|
|||
|
|
main_layout = QVBoxLayout(self)
|
|||
|
|
main_layout.setContentsMargins(24, 24, 24, 24) # 边距
|
|||
|
|
main_layout.setSpacing(16) # 控件间距
|
|||
|
|
|
|||
|
|
# 表单布局(管理标签和输入框)
|
|||
|
|
form_layout = QFormLayout()
|
|||
|
|
form_layout.setRowWrapPolicy(QFormLayout.DontWrapRows) # 不自动换行
|
|||
|
|
# 标签右对齐+垂直居中
|
|||
|
|
form_layout.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
|||
|
|
form_layout.setSpacing(12) # 表单行间距
|
|||
|
|
|
|||
|
|
# 1. 添加点位名称布局
|
|||
|
|
form_layout.addRow(StrongBodyLabel("点位名称"), self.name_combo)
|
|||
|
|
|
|||
|
|
# 2. 添加工具坐标布局
|
|||
|
|
tool_coord_layout = QHBoxLayout()
|
|||
|
|
tool_coord_layout.addWidget(self.tool_coord_spin)
|
|||
|
|
tool_coord_layout.addWidget(self.tool_coord_btn)
|
|||
|
|
form_layout.addRow(StrongBodyLabel("工具坐标id"), tool_coord_layout)
|
|||
|
|
|
|||
|
|
# 3. 添加工件坐标布局
|
|||
|
|
work_coord_layout = QHBoxLayout() # 工件坐标水平布局
|
|||
|
|
work_coord_layout.addWidget(self.work_coord_spin)
|
|||
|
|
work_coord_layout.addWidget(self.work_coord_btn)
|
|||
|
|
form_layout.addRow(StrongBodyLabel("工件坐标id"), work_coord_layout)
|
|||
|
|
|
|||
|
|
# 4-9 关节坐标布局
|
|||
|
|
joint_control_layout = QHBoxLayout()
|
|||
|
|
|
|||
|
|
# 左侧:关节角输入(J1-J6)
|
|||
|
|
joint_input_layout = QFormLayout()
|
|||
|
|
joint_input_layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
|
|||
|
|
joint_input_layout.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
|||
|
|
joint_input_layout.setSpacing(12)
|
|||
|
|
|
|||
|
|
for index in range(6):
|
|||
|
|
joint_input_layout.addRow(
|
|||
|
|
StrongBodyLabel(f"J{index + 1} (°)"), self.j_spins[index]
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 将关节坐标输入布局 添加到 关节坐标布局
|
|||
|
|
joint_control_layout.addLayout(joint_input_layout)
|
|||
|
|
|
|||
|
|
# 右侧:步长设置和获取按钮
|
|||
|
|
control_panel_layout = QVBoxLayout()
|
|||
|
|
control_panel_layout.setSpacing(16)
|
|||
|
|
step_layout = QVBoxLayout(self.step_group)
|
|||
|
|
step_layout.setContentsMargins(10, 15, 10, 15)
|
|||
|
|
step_layout.setSpacing(10)
|
|||
|
|
step_layout.addWidget(self.step_input)
|
|||
|
|
|
|||
|
|
# step_layout添加到控制面板布局
|
|||
|
|
control_panel_layout.addWidget(self.step_group)
|
|||
|
|
control_panel_layout.addWidget(self.get_values_btn)
|
|||
|
|
control_panel_layout.addStretch() # 拉伸项,使内容靠上
|
|||
|
|
|
|||
|
|
# 将 控制面板布局(右侧) 添加到 关节控制布局
|
|||
|
|
joint_control_layout.addLayout(control_panel_layout)
|
|||
|
|
|
|||
|
|
# 将关节控制水平布局添加到表单布局
|
|||
|
|
form_layout.addRow(StrongBodyLabel("关节坐标"), joint_control_layout)
|
|||
|
|
|
|||
|
|
# 10. 速度布局
|
|||
|
|
form_layout.addRow(StrongBodyLabel("速度 (%)"), self.approach_speed_spin)
|
|||
|
|
|
|||
|
|
# 11. 运动类型(下拉选择)布局
|
|||
|
|
form_layout.addRow(StrongBodyLabel("运动类型"), self.motion_type_combo)
|
|||
|
|
|
|||
|
|
# 12. "在此点" 平滑选择布局
|
|||
|
|
stop_layout = QHBoxLayout()
|
|||
|
|
stop_layout.addWidget(self.stop_radio)
|
|||
|
|
stop_layout.addWidget(self.smooth_radio)
|
|||
|
|
stop_layout.addWidget(self.smooth_ms_spin)
|
|||
|
|
stop_layout.addWidget(StrongBodyLabel("ms"))
|
|||
|
|
stop_layout.setAlignment(Qt.AlignLeft) # 与标签左对齐
|
|||
|
|
form_layout.addRow(StrongBodyLabel("在此点"), stop_layout)
|
|||
|
|
|
|||
|
|
# 将表单布局添加到主布局
|
|||
|
|
main_layout.addLayout(form_layout)
|
|||
|
|
|
|||
|
|
# 13. 底部按钮布局(居中显示)
|
|||
|
|
btn_layout = QHBoxLayout()
|
|||
|
|
btn_layout.setAlignment(Qt.AlignHCenter) # 水平居中
|
|||
|
|
btn_layout.addWidget(self.apply_btn)
|
|||
|
|
|
|||
|
|
# 将底部按钮布局添加到主布局
|
|||
|
|
main_layout.addLayout(btn_layout)
|
|||
|
|
|
|||
|
|
# 让表单控件顶部对齐
|
|||
|
|
main_layout.addStretch(1)
|
|||
|
|
|
|||
|
|
def __bind(self):
|
|||
|
|
# 更新 J1 到 J6 的步长
|
|||
|
|
self.step_input.valueChanged.connect(self.onUpdateStepSize)
|
|||
|
|
|
|||
|
|
# 获取 J1 到 J6 的值(外部相关)
|
|||
|
|
self.get_values_btn.clicked.connect(self.onGetJointValues)
|
|||
|
|
|
|||
|
|
# 调整平滑时间设置控件可不可用
|
|||
|
|
self.stop_radio.toggled.connect(
|
|||
|
|
lambda checked: self.smooth_ms_spin.setEnabled(not checked)
|
|||
|
|
)
|
|||
|
|
self.smooth_radio.toggled.connect(
|
|||
|
|
lambda checked: self.smooth_ms_spin.setEnabled(checked)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 应用按钮点击 (外部相关)
|
|||
|
|
self.apply_btn.clicked.connect(self.onApplyBtnClicked)
|
|||
|
|
|
|||
|
|
# 设置状态编辑框中的 点位的状态
|
|||
|
|
def setPointStateValue(self, pos_state_dict: dict):
|
|||
|
|
# 设置除了点位名字之外的所有 点位状态的值
|
|||
|
|
self.approach_speed_spin.setValue(pos_state_dict["speed"])
|
|||
|
|
self.tool_coord_spin.setValue(pos_state_dict["tool_id"])
|
|||
|
|
self.work_coord_spin.setValue(pos_state_dict["work_id"])
|
|||
|
|
for index in range(6):
|
|||
|
|
self.j_spins[index].setValue(pos_state_dict["joint_values"][index])
|
|||
|
|
|
|||
|
|
# 运动状态设置
|
|||
|
|
# 查找目标文本对应的索引
|
|||
|
|
target_motion_type = pos_state_dict["motion_type"]
|
|||
|
|
# 1. 查找目标文本对应的索引
|
|||
|
|
motion_index = self.motion_type_combo.findText(target_motion_type)
|
|||
|
|
# 2. 若未找到(索引=-1),默认选中第0项;否则选中对应索引
|
|||
|
|
if motion_index == -1:
|
|||
|
|
self.motion_type_combo.setCurrentIndex(0)
|
|||
|
|
else:
|
|||
|
|
self.motion_type_combo.setCurrentIndex(motion_index)
|
|||
|
|
|
|||
|
|
if pos_state_dict["blend_time"] == -1: # 此时为 停止
|
|||
|
|
self.stop_radio.setChecked(True)
|
|||
|
|
else:
|
|||
|
|
self.smooth_radio.setChecked(True)
|
|||
|
|
self.smooth_ms_spin.setValue(pos_state_dict["blend_time"])
|
|||
|
|
|
|||
|
|
def onUpdateStepSize(self, value):
|
|||
|
|
"""更新所有关节角输入框的步长"""
|
|||
|
|
for spin in self.j_spins:
|
|||
|
|
spin.setSingleStep(value)
|
|||
|
|
|
|||
|
|
def onGetJointValues(self):
|
|||
|
|
"""获取J1-J6的值(这里用示例值模拟)"""
|
|||
|
|
# 实际应用中,这里应该从设备或其他数据源获取值
|
|||
|
|
# 这里用随机值模拟
|
|||
|
|
import random
|
|||
|
|
|
|||
|
|
for i in range(6):
|
|||
|
|
# 生成一个-180到180之间的随机数,保留3位小数
|
|||
|
|
value = round(random.uniform(-180, 180), 3)
|
|||
|
|
self.j_spins[i].setValue(value)
|
|||
|
|
print("已获取并更新J1-J6的值")
|
|||
|
|
|
|||
|
|
def onApplyBtnClicked(self):
|
|||
|
|
"""应用按钮点击事件处理"""
|
|||
|
|
# 1、获取点名称
|
|||
|
|
pos_name = self.name_combo.text()
|
|||
|
|
# 2、速度
|
|||
|
|
speed = self.approach_speed_spin.value()
|
|||
|
|
# 3、tool_id
|
|||
|
|
tool_id = self.tool_coord_spin.value()
|
|||
|
|
# 4、work_id
|
|||
|
|
work_id = self.work_coord_spin.value()
|
|||
|
|
# 5-10、所有关节坐标 J1 到 J6
|
|||
|
|
joint_values = [spin.value() for spin in self.j_spins]
|
|||
|
|
# 11、运动类型 (直线、 曲线中间点、 曲线终点、 自由路径)
|
|||
|
|
motion_type = self.motion_type_combo.currentText()
|
|||
|
|
# 12、平滑时间(停止=-1,否则取输入值)
|
|||
|
|
blend_time = -1 if self.stop_radio.isChecked() else self.smooth_ms_spin.value()
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
point_state = PointState(
|
|||
|
|
pos_name=pos_name,
|
|||
|
|
speed=speed,
|
|||
|
|
tool_id=tool_id,
|
|||
|
|
work_id=work_id,
|
|||
|
|
joint_values=joint_values,
|
|||
|
|
motion_type=motion_type,
|
|||
|
|
blend_time=blend_time,
|
|||
|
|
)
|
|||
|
|
# print("状态编辑结果:", point_state.__str__())
|
|||
|
|
|
|||
|
|
# 发送信号给 表单窗口 CoordinateTableWidget对象
|
|||
|
|
self.point_state_applied.emit(self.selected_row_idx, point_state)
|
|||
|
|
|
|||
|
|
# 关闭状态编辑窗口
|
|||
|
|
self.close()
|
|||
|
|
except ValueError as e:
|
|||
|
|
# 捕获校验错误,弹窗提示用户
|
|||
|
|
MessageBox("状态错误", str(e), self).exec()
|
|||
|
|
|
|||
|
|
|
|||
|
|
# if __name__ == "__main__":
|
|||
|
|
# app = QApplication([])
|
|||
|
|
# setTheme(Theme.DARK) # 设置浅色主题(可选:Theme.DARK)
|
|||
|
|
# widget = StatusEditWidget()
|
|||
|
|
# widget.show() # 显示窗口
|
|||
|
|
# app.exec()
|