点位设置界面增加了状态设置,从数据库加载状态

This commit is contained in:
2025-09-10 20:27:51 +08:00
parent 3c9784b362
commit b90395ea24
3 changed files with 789 additions and 43 deletions

View File

@ -1,6 +1,7 @@
# coding:utf-8
import sys
import os
import time
from PySide6.QtCore import (
Qt,
QSize,
@ -55,6 +56,9 @@ from ..common.style_sheet import StyleSheet
import sqlite3
from .mi_an.status_edit_dialog import StatusEditDialog
from ..model.point_state import PointState
class CoodFormsInterface(GalleryInterface):
"""Cood Forms interface"""
@ -155,6 +159,8 @@ class FormDatabase:
j4 DOUBLE NOT NULL DEFAULT -9999.0, --关节角度j4单位°
j5 DOUBLE NOT NULL DEFAULT -9999.0, --关节角度j5单位°
j6 DOUBLE NOT NULL DEFAULT -9999.0, --关节角度j6单位°
motion_type VARCHAR(20) NOT NULL DEFAULT '直线', -- 运动类型
blend_time INTEGER NOT NULL DEFAULT -1, -- 平滑时间,-1默认停止
ext1 DOUBLE, --扩展字段1
ext2 INTEGER, --扩展字段2
ext3 TEXT, --扩展字段3
@ -259,27 +265,139 @@ class FormDatabase:
raise exc
def insert_form_data(self, form_id, data_rows):
# 备注: data_rows 中一行的数据 包含 row_index, x, y, z, rx, ry, rz, name, pos_state
# pos_state (点位状态, 字典类型) 包含 pos_name,speed, tool_id, work_id, j1-j6, motion_type, blend_time (状态编辑界面设置完成后插入)
"""批量插入form_data表"""
try:
cursor = self.__connect()
for row in data_rows:
# row格式: [row_idx, x, y, z, rx, ry, rz]
cursor.execute(
"""INSERT INTO form_data
(form_id, row_index, x, y, z, rx, ry, rz, name)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(
form_id,
row[0],
row[1],
row[2],
row[3],
row[4],
row[5],
row[6],
row[7],
),
)
# row格式: [row_idx, x, y, z, rx, ry, rz, name, pos_state_dict]
"""
row索引为8的列为 点位状态的字典 (pos_state为字典类型)
格式如:
{'pos_name': 'normal', 'speed': 20.0, 'tool_id': 0, 'work_id': 0,
'joint_values': [95.261, 82.247, -180.0, -75.121, -84.143, -15.421],
'motion_type': '直线', 'blend_time': -1}
"""
pos_state_dict = row[8]
if not pos_state_dict: # 没有设置点位状态,点位状态使用默认值
cursor.execute(
"""INSERT INTO form_data
(form_id, row_index, x, y, z, rx, ry, rz, name)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(
form_id,
row[0],
row[1],
row[2],
row[3],
row[4],
row[5],
row[6],
row[7],
),
)
else:
# 从状态字典pos_state_dict中提取数据
pos_name = pos_state_dict.get("pos_name", "normal")
speed = pos_state_dict.get("speed", 20.0)
tool_id = pos_state_dict.get("tool_id", 0)
workpiece_id = pos_state_dict.get("work_id", 0)
joint_values = pos_state_dict.get("joint_values", [-9999.0] * 6)
j1, j2, j3, j4, j5, j6 = joint_values[:6]
motion_type = pos_state_dict.get("motion_type", "直线")
blend_time = pos_state_dict.get("blend_time", -1)
cursor.execute(
"""INSERT INTO form_data
(form_id, row_index, x, y, z, rx, ry, rz, name,
speed, tool_id, workpiece_id, j1, j2, j3, j4, j5, j6, motion_type, blend_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(
form_id, # 1. form_id
row[0], # 2. row_index
row[1], # 3. x
row[2], # 4. y
row[3], # 5. z
row[4], # 6. rx
row[5], # 7. ry
row[6], # 8. rz
pos_name, # 9. name # 以点位状态中的 pos_name 为准
speed, # 10. speed
tool_id, # 11. tool_id
workpiece_id, # 12. workpiece_id
j1, # 13. j1
j2, # 14. j2
j3, # 15. j3
j4, # 16. j4
j5, # 17. j5
j6, # 18. j6
motion_type, # 19. motion_type
blend_time, # 20. blend_time
),
)
except Exception as exc:
self.rollback_and_close()
raise exc
def get_form_data(self, form_id):
"""
根据form_id查询表单数据, 返回所有的表单数据
返回格式为:
data_rows = [
[row_idx, x, y, z, rx, ry, rz, name, pos_state_dict],
...
]
"""
try:
cursor = self.__connect()
# 查询指定form_id的所有数据行并按row_index排序
cursor.execute(
"""SELECT
row_index, x, y, z, rx, ry, rz, name,
speed, tool_id, workpiece_id, j1, j2, j3, j4, j5, j6,
motion_type, blend_time
FROM form_data
WHERE form_id = ?
ORDER BY row_index""",
(form_id,),
)
data_rows = []
# 遍历查询结果
for row in cursor.fetchall():
# 解析基础坐标数据
row_idx = row[0]
x, y, z, rx, ry, rz = row[1:7]
name = row[7]
# 解析 点位状态数据并构建字典
speed = row[8]
tool_id = row[9]
work_id = row[10] #
j1, j2, j3, j4, j5, j6 = row[11:17]
motion_type = row[17]
blend_time = row[18]
pos_state_dict = {
"pos_name": name, # 保持与插入时一致使用name作为pos_name
"speed": speed,
"tool_id": tool_id,
"work_id": work_id,
"joint_values": [j1, j2, j3, j4, j5, j6],
"motion_type": motion_type,
"blend_time": blend_time,
}
# 构建一行的数据
data_row = [row_idx, x, y, z, rx, ry, rz, name, pos_state_dict]
data_rows.append(data_row)
return data_rows
except Exception as exc:
self.rollback_and_close()
raise exc
@ -354,7 +472,7 @@ class DatabaseSaveThread(QThread):
super().__init__()
self.db_path = db_path
self.form_name = form_name
self.data_rows = data_rows # 线程传递过来的UI数据
self.data_rows = data_rows # UI线程传递过来的UI数据
self.user_choice = None # 存储用户选择True=覆盖False=取消)
self.mutex = QMutex()
self.condition = QWaitCondition()
@ -383,12 +501,15 @@ class DatabaseSaveThread(QThread):
# 用户选择覆盖:删除旧数据
db.delete_form_data(exist_form_id)
# 插入新数据复用exist_form_id
# 新增对点位状态的保存
db.insert_form_data(exist_form_id, self.data_rows)
self.result_signal.emit(f"表单「{self.form_name}」已覆盖并保存")
else:
# 表单不存在:直接新增
# 新增对点位状态的保存
new_form_id = db.add_new_form(self.form_name)
db.insert_form_data(new_form_id, self.data_rows)
self.result_signal.emit(f"表单「{self.form_name}」已保存")
@ -418,11 +539,11 @@ class DatabaseSaveThread(QThread):
# 数据库读取操作线程
class DatabaseReadThread(QThread):
# 信号1: 从form_info 读取所有的 id-name 发送
# 信号1: 从form_info 读取所有的 id-name 发送
# [(form_id1, form_name1),...]
form_id_name_signal = Signal(list)
# 信号2: 从 from_data 读取所有的 选择的表单的数据发送
# 信号2: 从 from_data 读取所有的 选择的表单的数据发送
# {form_name1: data_rows1, ...}
form_data_signal = Signal(dict)
@ -458,7 +579,7 @@ class DatabaseReadThread(QThread):
form_date_dict = dict()
for form_id, form_name in self.selected_form_id_name:
data_rows = db.get_row_idx_coordinate_and_name(form_id)
data_rows = db.get_form_data(form_id)
form_date_dict[form_name] = data_rows
self.form_data_signal.emit(form_date_dict)
@ -499,6 +620,12 @@ class CoordinateTableWidget(QWidget):
self.has_valid_copy = False # 标志是否进行了有效的复制(复制了一行的数据)
self.setObjectName("CoordinateTableWidget")
# 状态编辑界面的临时数据
# 状态编辑后点击应用保存在这里
# 临时编辑数据:{标识符: 编辑后的状态(name、j1...)}
# name需要特殊处理
self.edited_state_dict = {}
# 主布局
self.mainLayout = QVBoxLayout(self)
self.mainLayout.setContentsMargins(30, 30, 30, 30)
@ -536,9 +663,12 @@ class CoordinateTableWidget(QWidget):
if self.initRowCount:
self.table.setRowCount(5) # 表的行数
self.table.setColumnCount(7) # 表的列数
# 新增第八列保存 毫秒级时间戳
self.table.setColumnCount(8) # 表的列数
self.table.setHorizontalHeaderLabels(["x", "y", "z", "rx", "ry", "rz", "name"])
self.table.setHorizontalHeaderLabels(
["x", "y", "z", "rx", "ry", "rz", "name", "timestamp"]
)
self.table.horizontalHeader().setStyleSheet(
"QHeaderView::section {font-size: 19px; font-weight: bold;}"
)
@ -546,6 +676,9 @@ class CoordinateTableWidget(QWidget):
"QHeaderView::section {font-size: 14px; font-weight: bold;}"
)
# 第八列索引7不显示 (通常不需要显示时间戳[唯一标识]
self.table.setColumnHidden(7, True)
if self.hideVHeader:
self.table.verticalHeader().hide() # 隐藏行标题(行名)
@ -608,7 +741,7 @@ class CoordinateTableWidget(QWidget):
# 状态编辑按钮
self.stateEditBtn = PushButton("状态编辑")
# self.stateEditBtn.clicked.connect(self.moveRowDown)
self.stateEditBtn.clicked.connect(self.onStateEdit)
btnLayout2.addWidget(self.stateEditBtn)
self.mainLayout.addLayout(btnLayout) # 添加 按钮布局一
@ -616,13 +749,14 @@ class CoordinateTableWidget(QWidget):
# 数据保存到数据库,获取数据时调用
def get_ui_data(self):
"""从表格UI中获取数据 [[行索引row_idx, x, y, z, rx, ry, rz, name], ......]"""
"""获取数据 [[行索引row_idx, x, y, z, rx, ry, rz, name, pos_state], ......]"""
row_count = self.table.rowCount()
# column_count = self.table.columnCount()
# 这里的 data_rows 保存 [[行索引row_idx, x, y, z, rx, ry, rz, name, pos_state], ......]
data_rows = []
for row_idx in range(row_count):
row_data = [row_idx] # 先保存行索引
is_valid_row = True # 标记当前行是否完全有效
is_valid_cood = True # 标记当前行的坐标是否完全有效
# 这里只有前6列是 x, y, z, rx, ry, rz
# 目前 从ui获取的 需要保存到数据库的 只有 x, y, z, rx, ry, rz
@ -634,7 +768,7 @@ class CoordinateTableWidget(QWidget):
# 对于非法数据 和 不完整数据,不保存在数据库
# 1、判断填写的 x, y, z, rx, ry, rz中是否有为空的坐标
if not item or not item.text().strip():
is_valid_row = False
is_valid_cood = False
break # 跳过这一行数据,保存下一行数据
# 2、检查填写的坐标是否都为数字
@ -642,24 +776,40 @@ class CoordinateTableWidget(QWidget):
try:
coord_num = float(coord_str) # 尝试转换为数字
except ValueError:
is_valid_row = False
is_valid_cood = False
break # 跳过这一行数据,保存下一行数据
# 保存单个坐标,浮点类型
row_data.append(coord_num)
# 新增:点位名字
# 增加点位名字的判断,当点位名 为空时,使用默认的 "normal"
name_idx = 6 # 目前的点位名的列索引为6 (索引从0开始)
name_item = self.table.item(row_idx, name_idx)
if not name_item or not name_item.text().strip():
row_data.append("normal")
else:
pos_name = name_item.text().strip()
row_data.append(pos_name)
# 前面的坐标数据都有效,才保存到数据库
if is_valid_cood:
# 新增:点位名字
# 增加点位名字的判断,当点位名 为空时,使用默认的 "normal"
name_idx = 6 # 目前的点位名的列索引为6 (索引从0开始)
name_item = self.table.item(row_idx, name_idx)
if not name_item or not name_item.text().strip():
row_data.append("normal")
else:
pos_name = name_item.text().strip()
row_data.append(pos_name)
# 放入这一行的数据 行索引row_idx, x, y, z, rx, ry, rz, name
if is_valid_row: # 有效,才保存到数据库
# 新增:点位状态 pos_state, pos_state的类型为字典
timestamp_idx = 7 # 目前的时间戳的列索引为7
timestamp_item = self.table.item(row_idx, timestamp_idx)
timestamp = (
int(timestamp_item.text().strip())
if (timestamp_item and timestamp_item.text().strip())
else -1 # 表示没有进行状态编辑
)
# 从edited_state_dict取 点位状态
state_dict = (
self.edited_state_dict.get(timestamp, {}) if timestamp != -1 else {}
) # 点位状态的字典
row_data.append(state_dict)
# 放入这一行的数据 行索引row_idx, x, y, z, rx, ry, rz, name, pos_state
data_rows.append(row_data)
# print("data_rows", data_rows)
@ -723,12 +873,14 @@ class CoordinateTableWidget(QWidget):
self.table.setVerticalHeaderItem(row, header_item)
name_idx = 6 # 点位名的列索引号
for col in range(self.table.columnCount()):
item = (
QTableWidgetItem("")
if col != name_idx
else QTableWidgetItem("normal")
)
item.setTextAlignment(Qt.AlignCenter)
self.setThemeTextColor(item)
self.table.setItem(row, col, item)
@ -745,6 +897,7 @@ class CoordinateTableWidget(QWidget):
msg_box.setStyleSheet("QLabel{color: black;}")
msg_box.exec()
# 外界用于设置坐标的接口
def update_table_data(self, positionList=None):
selectedRows = self.getSelectedRows()
for row in selectedRows:
@ -899,6 +1052,7 @@ class CoordinateTableWidget(QWidget):
return
# 获取UI数据
# 目前,一行的点位数据包含了 点位状态 pos_state_dict
data_rows = self.get_ui_data()
if not data_rows:
self.status_label.setText("没有数据可以保存, 请检查数据有效性!!!")
@ -1059,10 +1213,77 @@ class CoordinateTableWidget(QWidget):
# 5. 更新状态提示
self.status_label.setText(f"{current_row+1}行已下移至第{target_row+1}")
# 状态编辑
def onStateEdit(self):
selected_rows = self.getSelectedRows()
# 目前只支持对一行进行状态编辑
if len(selected_rows) != 1:
self.showNoSelectWarning("请仅选中一行进行状态编辑!!!")
return
# 需要进行状态编辑的行的 行索引
selected_row_idx = selected_rows[0]
# 获取到该行的 点位名称列索引为6
name_col_idx = 6
name_item = self.table.item(selected_row_idx, name_col_idx)
# 此时的点位名 为空,则使用默认的 mormal表示普通点
position_name = (
name_item.text().strip()
if name_item and name_item.text().strip()
else "normal"
)
# print(position_name)
# 状态编辑对话框
# 传入 点位名称, 选中行的行索引 (一行)
status_diog = StatusEditDialog(position_name, selected_row_idx, self)
# 获取该行的时间戳(唯一标识)[重要]
# 时间戳的 列索引为7
timestamp_col_idx = 7
timestamp_item = self.table.item(selected_row_idx, timestamp_col_idx)
# 如果存在时间戳,那么就需要读取相应的 点位状态,并设置到状态编辑界面
if timestamp_item and timestamp_item.text().strip():
timestamp = int(timestamp_item.text().strip())
status_diog.setPointStateValue(self.edited_state_dict[timestamp])
# 状态编辑界面点击应用按钮,保存状态编辑数据
status_diog.point_state_applied.connect(self.onSaveStateEdit)
status_diog.exec()
# 保存 编辑的 点位状态
def onSaveStateEdit(self, selected_row_idx: int, point_state: PointState):
# 需要保存为 {唯一的值1 : 状态字典1, 唯一的值2 : 状态字典2, ...}
# 唯一的值?时间戳作为唯一值
# 1、时间戳
unique_key = int(time.time() * 1000) # 毫秒级时间戳
self.edited_state_dict[unique_key] = point_state.to_dict()
# 2. 设置当前行的隐藏列列索引为7为 当前时间戳
timestamp_idx = 7 # 当前,时间戳在表格中的索引为 7 (点位名称的后一个)
timestamp_item = QTableWidgetItem(str(unique_key))
timestamp_item.setTextAlignment(Qt.AlignCenter)
self.setThemeTextColor(timestamp_item)
self.table.setItem(selected_row_idx, timestamp_idx, timestamp_item)
# 3、同步点位的名称显示到表格
name_idx = 6 # 当前,点位名称在表格中的索引为 6
name_item = QTableWidgetItem(str(point_state.pos_name))
name_item.setTextAlignment(Qt.AlignCenter)
self.setThemeTextColor(name_item)
self.table.setItem(selected_row_idx, name_idx, name_item)
# print("onSaveStateEdit\n", self.edited_state_dict)
# ================交换两行数据=======================
def _swapTwoRows(self, row1, row2):
"""交换表格中两行的所有列数据包括坐标和name列"""
total_cols = self.table.columnCount() # 7x/y/z/rx/ry/rz/name
total_cols = self.table.columnCount() # 8x/y/z/rx/ry/rz/name/timestamp
for col in range(total_cols):
# 1. 取出两行当前列的itemtakeItem会移除原位置item避免引用冲突
@ -1153,6 +1374,7 @@ class CoordinateFormsWidget(QWidget):
self.initFirstForm()
# 初始化样式,应用主题样式
# 后续使用 qss 文件
self.applyThemeStyle()
def applyThemeStyle(self):
@ -1510,7 +1732,7 @@ class CoordinateFormsWidget(QWidget):
# 由读取的数据生成新表单
def generateNewForms(self, form_data):
"""根据读取到的数据生成新表单(UI线程处理)"""
# form_data格式{form_name: [(row_idx, x, y, z, rx, ry, rz, name), ...], ...}
# form_data格式{form_name: [(row_idx, x, y, z, rx, ry, rz, name, pos_state_dict), ...], ...}
# print("form_data", form_data)
for form_name, data_rows in form_data.items():
# 创建新表单
@ -1522,12 +1744,18 @@ class CoordinateFormsWidget(QWidget):
new_form.move_to_coodinate_signal.connect(self.form_move_signal)
# 填充数据到新表单(包含点位名)
for row_idx, x, y, z, rx, ry, rz, name in data_rows:
for row_idx, x, y, z, rx, ry, rz, name, pos_state_dict in data_rows:
# print(row_idx, x, y, z, rx, ry, rz)
# 如果数单行数超过表格行数,自动添加行
while row_idx >= new_form.table.rowCount():
new_form.addNewRow()
# 这里获取一个唯一值作为 点位状态的标识 (就是前面的时间戳)
unique_key = self.getPositionStateUniqueKey(row_idx)
# 将获取到的点位状态 保存到 创建的新表单的edited_state_dict中
new_form.edited_state_dict[unique_key] = pos_state_dict
# 填充单元格
new_form.table.setItem(row_idx, 0, QTableWidgetItem(str(x)))
new_form.table.setItem(row_idx, 1, QTableWidgetItem(str(y)))
@ -1537,7 +1765,10 @@ class CoordinateFormsWidget(QWidget):
new_form.table.setItem(row_idx, 5, QTableWidgetItem(str(rz)))
new_form.table.setItem(row_idx, 6, QTableWidgetItem(str(name)))
# 将填充的数据设置居中显示,一共是 0 到 6如上共7列
# 时间戳(唯一标识)
new_form.table.setItem(row_idx, 7, QTableWidgetItem(str(unique_key)))
# 将填充的数据设置居中显示,一共是 0 到 6如上共7列 (不包括时间戳,时间戳不显示)
for col_idx in range(7):
new_form.table.item(row_idx, col_idx).setTextAlignment(
Qt.AlignCenter
@ -1550,6 +1781,11 @@ class CoordinateFormsWidget(QWidget):
)
self.formStack.addWidget(new_form)
# 点状态唯一标识
def getPositionStateUniqueKey(self, row_idx):
unique_key = int(time.time() * 1000) + row_idx
return unique_key
# 更新表单中选中行数据 x,y,z,rx,ry,rz
# def test(form_obj: CoordinateTableWidget):