Files
fluent_widgets_pyside6/app/view/mi_an/status_edit_dialog.py

383 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()