Files
wire_controlsystem/UI/ui.py

560 lines
22 KiB
Python
Raw Permalink Normal View History

2026-01-05 18:11:56 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
# @Time : 2025/12/8 14:19
# @Author : reenrr
# @File : ui.py
# @Description: 线条长度标记与电机位置控制系统主界面
'''
import sys
import time
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QHBoxLayout, QLabel, QPushButton,
QGroupBox, QMessageBox, QComboBox, QTextEdit,
2026-01-07 18:11:27 +08:00
QTableWidget, QTableWidgetItem)
2026-01-05 18:11:56 +08:00
from PySide6.QtCore import QTimer, Qt, Signal, Slot
from PySide6.QtGui import (QFont, QPalette, QColor)
2026-01-07 18:11:27 +08:00
from ui_3d import Target3DWidgetA, Target3DWidgetB
2026-01-05 18:11:56 +08:00
class LengthMotorController(QMainWindow):
"""线条长度标记与电机位置控制系统主界面"""
record_data_signal = Signal(float, float, float, float)
line_select_signal = Signal(str, float, float, float, float)
def __init__(self):
super().__init__()
self.setWindowTitle("线条长度标记与电机位置控制系统")
self.setMinimumSize(1200, 700)
2026-01-07 18:11:27 +08:00
self.font1 = QFont("Microsoft YaHei", 14)
2026-01-05 18:11:56 +08:00
self.font2 = QFont("Microsoft YaHei", 12)
self.recorded_data = []
# 线条A参数
self.line_a_params = {
"类型1": (0.5, 5, 0.1, 25),
"类型2": (1.0, 10, 0.2, 30),
"类型3": (1.2, 8, 0.15, 28),
"类型4": (1.8, 12, 0.25, 32),
"类型5": (0.8, 7, 0.12, 26),
"类型6": (1.4, 14, 0.18, 31),
"类型7": (1.6, 16, 0.22, 33),
"类型8": (2.0, 18, 0.28, 34)
}
# 线条B参数
self.line_b_params = {
"类型1": (1.5, 15, 0.3, 35),
"类型2": (2.0, 20, 0.4, 40),
"类型3": (1.6, 18, 0.35, 38),
"类型4": (2.2, 22, 0.45, 42),
"类型5": (1.7, 25, 0.32, 36),
"类型6": (1.9, 28, 0.38, 39),
"类型7": (2.1, 30, 0.42, 41),
"类型8": (2.3, 32, 0.48, 43)
}
# 当前选中的线条类型
self.selected_line_a = "类型1"
self.selected_line_b = "类型1"
self._setup_style()
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(20)
main_layout.setContentsMargins(30, 30, 30, 30)
# 1. 顶部标题(替代原实时时间)
self._create_title(main_layout)
2026-01-07 18:11:27 +08:00
# 2. 下方左右分栏:左侧线条选择区,右侧(实时时间 + 参数 + 3D
2026-01-05 18:11:56 +08:00
bottom_h_layout = QHBoxLayout()
bottom_h_layout.setSpacing(20)
2026-01-07 18:11:27 +08:00
# 左侧:线条选择区(表格)
2026-01-05 18:11:56 +08:00
self._create_line_select_table(bottom_h_layout)
2026-01-07 18:11:27 +08:00
# 右侧:实时时间 + 参数设置 + 3D显示区
2026-01-05 18:11:56 +08:00
self._create_right_area(bottom_h_layout)
main_layout.addLayout(bottom_h_layout)
self._init_timer()
self._connect_signals()
def _setup_style(self):
"""设置界面样式"""
palette = QPalette()
palette.setColor(QPalette.Window, QColor(245, 245, 247))
palette.setColor(QPalette.WindowText, QColor(30, 30, 30))
palette.setColor(QPalette.Button, QColor(66, 133, 244))
palette.setColor(QPalette.ButtonText, Qt.white)
self.setPalette(palette)
self.setStyleSheet("""
QGroupBox {
font-weight: bold;
border: 2px solid #4285F4;
border-radius: 8px;
margin-top: 10px;
padding-top: 10px;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 8px 0 8px;
}
QPushButton {
border-radius: 6px;
padding: 8px 16px;
font-size: 12pt;
}
QPushButton:hover {
background-color: #5294FF;
}
QComboBox {
border: 2px solid #DDDDDD;
border-radius: 6px;
padding: 8px;
font-size: 10pt;
}
QComboBox:focus {
border-color: #4285F4;
outline: none;
}
QTextEdit {
border: 2px solid #DDDDDD;
border-radius: 6px;
background-color: white;
font-size: 11pt;
}
QTableWidget {
border: 2px solid #DDDDDD;
border-radius: 6px;
font-size: 11pt;
}
QTableWidget::item:selected {
background-color: #E1F5FE;
color: #212121;
}
""")
def _create_title(self, parent_layout):
"""顶部标题(替代原实时时间)"""
title_label = QLabel("线条长度标记与电机位置控制系统")
title_label.setFont(QFont("Microsoft YaHei", 20, QFont.Bold))
title_label.setAlignment(Qt.AlignCenter)
parent_layout.addWidget(title_label)
def _create_line_select_table(self, parent_layout):
2026-01-07 18:11:27 +08:00
"""左侧线条选择区(表格)"""
2026-01-05 18:11:56 +08:00
line_select_group = QGroupBox("线条选择(参数预览)")
line_select_group.setFont(self.font1)
line_select_group.setMinimumWidth(self.width() * 0.5)
select_layout = QVBoxLayout(line_select_group)
select_layout.setSpacing(20)
select_layout.setContentsMargins(20, 20, 20, 20)
# 线条A表格
a_label = QLabel("线条A参数表")
a_label.setFont(self.font2)
self.table_line_a = QTableWidget()
self.table_line_a.setRowCount(len(self.line_a_params))
2026-01-07 18:11:27 +08:00
self.table_line_a.setColumnCount(5)
2026-01-05 18:11:56 +08:00
self.table_line_a.setHorizontalHeaderLabels(["类型", "参数a", "参数b", "参数c", "参数d"])
for row, (type_name, params) in enumerate(self.line_a_params.items()):
self.table_line_a.setItem(row, 0, QTableWidgetItem(type_name))
self.table_line_a.setItem(row, 1, QTableWidgetItem(str(params[0])))
self.table_line_a.setItem(row, 2, QTableWidgetItem(str(params[1])))
self.table_line_a.setItem(row, 3, QTableWidgetItem(str(params[2])))
self.table_line_a.setItem(row, 4, QTableWidgetItem(str(params[3])))
2026-01-07 18:11:27 +08:00
self.table_line_a.setFixedHeight(280)
self.table_line_a.setEditTriggers(QTableWidget.NoEditTriggers)
self.table_line_a.setSelectionBehavior(QTableWidget.SelectRows)
self.table_line_a.selectRow(0)
2026-01-05 18:11:56 +08:00
self.table_line_a.itemSelectionChanged.connect(self._on_line_a_select)
# 线条B表格
b_label = QLabel("线条B参数表")
b_label.setFont(self.font2)
self.table_line_b = QTableWidget()
self.table_line_b.setRowCount(len(self.line_b_params))
self.table_line_b.setColumnCount(5)
self.table_line_b.setHorizontalHeaderLabels(["类型", "参数a", "参数b", "参数c", "参数d"])
for row, (type_name, params) in enumerate(self.line_b_params.items()):
self.table_line_b.setItem(row, 0, QTableWidgetItem(type_name))
self.table_line_b.setItem(row, 1, QTableWidgetItem(str(params[0])))
self.table_line_b.setItem(row, 2, QTableWidgetItem(str(params[1])))
self.table_line_b.setItem(row, 3, QTableWidgetItem(str(params[2])))
self.table_line_b.setItem(row, 4, QTableWidgetItem(str(params[3])))
2026-01-07 18:11:27 +08:00
self.table_line_b.setFixedHeight(260)
2026-01-05 18:11:56 +08:00
self.table_line_b.setEditTriggers(QTableWidget.NoEditTriggers)
self.table_line_b.setSelectionBehavior(QTableWidget.SelectRows)
self.table_line_b.selectRow(0)
self.table_line_b.itemSelectionChanged.connect(self._on_line_b_select)
# 导入按钮
import_layout = QHBoxLayout()
import_a_btn = QPushButton("导入线条A选中参数")
import_a_btn.setFont(self.font2)
import_a_btn.clicked.connect(lambda: self._import_line_param("A"))
import_b_btn = QPushButton("导入线条B选中参数")
import_b_btn.setFont(self.font2)
import_b_btn.clicked.connect(lambda: self._import_line_param("B"))
import_layout.addWidget(import_a_btn)
import_layout.addWidget(import_b_btn)
select_layout.addWidget(a_label)
select_layout.addWidget(self.table_line_a)
select_layout.addWidget(b_label)
select_layout.addWidget(self.table_line_b)
select_layout.addLayout(import_layout)
2026-01-07 18:11:27 +08:00
select_layout.addStretch()
2026-01-05 18:11:56 +08:00
parent_layout.addWidget(line_select_group)
def _on_line_a_select(self):
2026-01-07 18:11:27 +08:00
"""线条A表格选中行变化"""
selected_rows = self.table_line_a.selectedItems()
2026-01-05 18:11:56 +08:00
if selected_rows:
# 获取选中行的第一列(类型列)文本
row = selected_rows[0].row()
self.selected_line_a = self.table_line_a.item(row, 0).text()
def _on_line_b_select(self):
2026-01-07 18:11:27 +08:00
"""线条B表格选中行变化"""
selected_rows = self.table_line_b.selectedItems()
2026-01-05 18:11:56 +08:00
if selected_rows:
row = selected_rows[0].row()
self.selected_line_b = self.table_line_b.item(row, 0).text()
def _create_right_area(self, parent_layout):
2026-01-07 18:11:27 +08:00
"""右侧区域:实时时间 → 线条类型设置 → 3D显示"""
2026-01-05 18:11:56 +08:00
right_v_layout = QVBoxLayout()
right_v_layout.setSpacing(20)
# 1. 实时时间(移到右侧顶部)
time_group = QGroupBox("实时时间")
time_group.setFont(self.font1)
time_layout = QHBoxLayout(time_group)
self.time_label = QLabel()
self.time_label.setAlignment(Qt.AlignCenter)
self.time_label.setFont(QFont("Microsoft YaHei", 14, QFont.Bold))
self._update_current_time() # 手动触发一次,避免延迟
2026-01-05 18:11:56 +08:00
time_layout.addWidget(self.time_label)
right_v_layout.addWidget(time_group)
# 2. 线条类型设置区
control_group = QGroupBox("线条类型设置")
control_group.setFont(self.font1)
control_layout = QVBoxLayout(control_group)
control_layout.setSpacing(15)
control_layout.setContentsMargins(20, 20, 20, 20)
# 提取所有参数a/b/c/d的可能值
# 收集参数a的所有值
# 提取所有参数a/b/c/d的可能值统一格式
# 收集参数a的所有值保留2位小数去除末尾0
param_a_list = []
for params in self.line_a_params.values():
a = params[0]
a_str = f"{a:.2f}".rstrip('0').rstrip('.') if '.' in f"{a:.2f}" else f"{a:.2f}"
param_a_list.append(a_str)
for params in self.line_b_params.values():
a = params[0]
a_str = f"{a:.2f}".rstrip('0').rstrip('.') if '.' in f"{a:.2f}" else f"{a:.2f}"
param_a_list.append(a_str)
param_a_list = sorted(list(set(param_a_list)), key=lambda x: float(x))
# 收集参数b的所有值整数格式
param_b_list = []
for params in self.line_a_params.values():
b_str = f"{params[1]:.0f}"
param_b_list.append(b_str)
for params in self.line_b_params.values():
b_str = f"{params[1]:.0f}"
param_b_list.append(b_str)
param_b_list = sorted(list(set(param_b_list)), key=lambda x: int(x))
# 收集参数c的所有值保留2位小数去除末尾0
param_c_list = []
for params in self.line_a_params.values():
c = params[2]
c_str = f"{c:.2f}".rstrip('0').rstrip('.') if '.' in f"{c:.2f}" else f"{c:.2f}"
param_c_list.append(c_str)
for params in self.line_b_params.values():
c = params[2]
c_str = f"{c:.2f}".rstrip('0').rstrip('.') if '.' in f"{c:.2f}" else f"{c:.2f}"
param_c_list.append(c_str)
param_c_list = sorted(list(set(param_c_list)), key=lambda x: float(x))
# 收集参数d的所有值整数格式
param_d_list = []
for params in self.line_a_params.values():
d_str = f"{params[3]:.0f}"
param_d_list.append(d_str)
for params in self.line_b_params.values():
d_str = f"{params[3]:.0f}"
param_d_list.append(d_str)
param_d_list = sorted(list(set(param_d_list)), key=lambda x: int(x))
2026-01-07 18:11:27 +08:00
# 4个参数下拉框
2026-01-05 18:11:56 +08:00
param_layout = QHBoxLayout()
param_layout.setSpacing(20)
# 参数a
a_param_layout = QVBoxLayout()
label_a = QLabel("参数a:")
label_a.setFont(self.font2)
label_a.setAlignment(Qt.AlignCenter)
self.combo_a = QComboBox()
self.combo_a.addItems(param_a_list)
self.combo_a.setFixedWidth(100)
self.combo_a.setFont(self.font2)
a_param_layout.addWidget(label_a)
a_param_layout.addWidget(self.combo_a)
param_layout.addLayout(a_param_layout)
# 参数b
b_param_layout = QVBoxLayout()
label_b = QLabel("参数b:")
label_b.setFont(self.font2)
label_b.setAlignment(Qt.AlignCenter)
self.combo_b = QComboBox()
self.combo_b.addItems(param_b_list)
self.combo_b.setFixedWidth(100)
self.combo_b.setFont(self.font2)
b_param_layout.addWidget(label_b)
b_param_layout.addWidget(self.combo_b)
param_layout.addLayout(b_param_layout)
# 参数c
c_param_layout = QVBoxLayout()
label_c = QLabel("参数c:")
label_c.setFont(self.font2)
label_c.setAlignment(Qt.AlignCenter)
self.combo_c = QComboBox()
self.combo_c.addItems(param_c_list)
self.combo_c.setFixedWidth(100)
self.combo_c.setFont(self.font2)
c_param_layout.addWidget(label_c)
c_param_layout.addWidget(self.combo_c)
param_layout.addLayout(c_param_layout)
# 参数d
d_param_layout = QVBoxLayout()
label_d = QLabel("参数d:")
label_d.setFont(self.font2)
label_d.setAlignment(Qt.AlignCenter)
self.combo_d = QComboBox()
self.combo_d.addItems(param_d_list)
self.combo_d.setFixedWidth(100)
self.combo_d.setFont(self.font2)
d_param_layout.addWidget(label_d)
d_param_layout.addWidget(self.combo_d)
param_layout.addLayout(d_param_layout)
# 按钮行
button_layout = QHBoxLayout()
self.save_btn = QPushButton("保存设置")
self.save_btn.setFont(self.font2)
2026-01-07 18:11:27 +08:00
self.save_btn.setFixedSize(140, 45)
2026-01-05 18:11:56 +08:00
self.clear_btn = QPushButton("清空选择")
self.clear_btn.setFont(self.font2)
2026-01-07 18:11:27 +08:00
self.clear_btn.setFixedSize(140, 45)
2026-01-05 18:11:56 +08:00
self.export_btn = QPushButton("导出记录")
self.export_btn.setFont(self.font2)
2026-01-07 18:11:27 +08:00
self.export_btn.setFixedSize(140, 45)
2026-01-05 18:11:56 +08:00
button_layout.addStretch()
button_layout.addWidget(self.save_btn)
button_layout.addWidget(self.clear_btn)
button_layout.addWidget(self.export_btn)
button_layout.addStretch()
control_layout.addLayout(param_layout)
control_layout.addLayout(button_layout)
right_v_layout.addWidget(control_group)
2026-01-07 18:11:27 +08:00
# 3. 3D效果显示占位区
2026-01-05 18:11:56 +08:00
self._create_3d_placeholder(right_v_layout)
parent_layout.addLayout(right_v_layout)
def _create_3d_placeholder(self, parent_layout):
2026-01-07 18:11:27 +08:00
"""3D效果显示占位区缩小留白"""
display_group = QGroupBox("线条3D效果显示")
2026-01-05 18:11:56 +08:00
display_group.setFont(self.font1)
display_layout = QVBoxLayout(display_group)
display_layout.setContentsMargins(20, 20, 20, 20)
# 创建3D控件的容器
self.ui_3d_container = QWidget() # 空容器专门放3D控件
self.ui_3d_container_layout = QVBoxLayout(self.ui_3d_container)
self.ui_3d_container_layout.setContentsMargins(0, 0, 0, 0)
self.ui_3d_container.setFixedSize(500, 330)
2026-01-05 18:11:56 +08:00
# 初始化默认显示A控件
self.current_3d_widget = Target3DWidgetA()
self.ui_3d_container_layout.addWidget(self.current_3d_widget)
2026-01-05 18:11:56 +08:00
display_layout.addWidget(self.ui_3d_container)
2026-01-05 18:11:56 +08:00
parent_layout.addWidget(display_group)
def _switch_3d_widget(self, widget_type):
"""
动态切换3D控件
:param widget_type: "A""B"
"""
# 移除旧的3D控件
if self.current_3d_widget:
self.current_3d_widget.setParent(None) # 从布局中移除
self.current_3d_widget.deleteLater() # 释放资源
# 创建新的3D控件
if widget_type == "A":
self.current_3d_widget = Target3DWidgetA()
else:
self.current_3d_widget = Target3DWidgetB()
# 添加新控件到容器
self.ui_3d_container_layout.addWidget(self.current_3d_widget)
2026-01-05 18:11:56 +08:00
def _init_timer(self):
"""初始化定时器"""
self.timer = QTimer(self)
self.timer.timeout.connect(self._update_current_time)
self.timer.start(1000)
def _connect_signals(self):
"""连接信号与槽函数"""
self.save_btn.clicked.connect(self._save_settings)
self.clear_btn.clicked.connect(self._clear_inputs)
self.export_btn.clicked.connect(self._export_records)
self.record_data_signal.connect(self._log_param_change)
self.line_select_signal.connect(self._set_line_params)
def _update_current_time(self):
"""更新当前时间"""
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
self.time_label.setText(current_time)
2026-01-05 18:11:56 +08:00
@Slot(str)
def _import_line_param(self, line_type):
"""导入选中线条的参数"""
if line_type == "A":
selected_type = self.selected_line_a
params = self.line_a_params[selected_type]
line_name = f"线条A-{selected_type}"
# 切换为A控件
self._switch_3d_widget("A")
2026-01-05 18:11:56 +08:00
else:
selected_type = self.selected_line_b
params = self.line_b_params[selected_type]
line_name = f"线条B-{selected_type}"
# 切换为B控件
self._switch_3d_widget("B")
2026-01-05 18:11:56 +08:00
self.line_select_signal.emit(line_name, *params)
QMessageBox.information(self, "导入成功", f"已导入{line_name}的参数\n3D视图已切换为{line_type}类型")
2026-01-05 18:11:56 +08:00
@Slot(str, float, float, float, float)
def _set_line_params(self, line_name, a, b, c, d):
"""设置参数到下拉框(统一浮点数格式)"""
# 根据参数特性设置小数位a/c保留2位b/d保留0位整数
a_str = f"{a:.2f}".rstrip('0').rstrip('.') if '.' in f"{a:.2f}" else f"{a:.2f}"
b_str = f"{b:.0f}" # 参数b是整数5/7/8等
c_str = f"{c:.2f}".rstrip('0').rstrip('.') if '.' in f"{c:.2f}" else f"{c:.2f}"
d_str = f"{d:.0f}" # 参数d是整数25/26/28等
"""设置参数到下拉框"""
self.combo_a.setCurrentText(a_str)
self.combo_b.setCurrentText(b_str)
self.combo_c.setCurrentText(c_str)
self.combo_d.setCurrentText(d_str)
2026-01-05 18:11:56 +08:00
@Slot()
def _save_settings(self):
"""保存参数并触发信号"""
try:
param_a = float(self.combo_a.currentText())
param_b = float(self.combo_b.currentText())
param_c = float(self.combo_c.currentText())
param_d = float(self.combo_d.currentText())
except ValueError:
QMessageBox.critical(self, "输入错误", "参数格式无效!")
return
if param_a <= 0 or param_b <= 0 or param_c < 0 or param_d < 0:
QMessageBox.warning(self, "参数错误", "参数a/b必须大于0c/d不能小于0")
return
self.record_data_signal.emit(param_a, param_b, param_c, param_d)
QMessageBox.information(self, "保存成功",
f"已保存设置:\n参数a{param_a}\n参数b{param_b}\n参数c{param_c}\n参数d{param_d}")
@Slot(float, float, float, float)
def _log_param_change(self, a, b, c, d):
"""记录参数变化"""
self.recorded_data.append({
"a": a, "b": b, "c": c, "d": d,
"time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
})
@Slot()
def _clear_inputs(self):
"""清空所有选择"""
self.combo_a.setCurrentIndex(0)
self.combo_b.setCurrentIndex(0)
self.combo_c.setCurrentIndex(0)
self.combo_d.setCurrentIndex(0)
self.table_line_a.selectRow(0)
self.table_line_b.selectRow(0)
@Slot()
def _export_records(self):
"""导出参数记录"""
if not self.recorded_data:
QMessageBox.warning(self, "导出提示", "暂无记录可导出!")
return
try:
filename = f"线条参数记录_{time.strftime('%Y%m%d_%H%M%S')}.txt"
with open(filename, 'w', encoding='utf-8') as f:
f.write("线条参数记录\n")
f.write("=" * 60 + "\n")
f.write(f"导出时间:{time.strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("参数a\t参数b\t参数c\t参数d\t记录时间\n")
f.write("-" * 60 + "\n")
for data in self.recorded_data:
f.write(f"{data['a']:.2f}\t{data['b']:.2f}\t{data['c']:.2f}\t{data['d']:.2f}\t{data['time']}\n")
QMessageBox.information(self, "导出成功", f"记录已导出到:\n{filename}")
except Exception as e:
QMessageBox.critical(self, "导出失败", f"导出文件时出错:{str(e)}")
def closeEvent(self, event):
"""关闭窗口确认"""
reply = QMessageBox.question(self, "确认退出",
"是否确定退出程序?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
if reply == QMessageBox.Yes:
self.timer.stop()
event.accept()
else:
event.ignore()
# ----------界面对外接口-----------
def ui_main():
app = QApplication(sys.argv)
app.setApplicationName("长度电机控制器")
app.setApplicationVersion("1.0.0")
window = LengthMotorController()
window.show()
sys.exit(app.exec())
# ----------测试接口--------------
if __name__ == "__main__":
ui_main()