一个小小的pyside6项目,TDK程控电源的上位机

       起因是实验室里平常涉及到大量的第三方模块测试,这些模块的电压可能不同,同时也是测试它的电流和功耗,所以购买了TDK的程控电源。TDK是电源的大厂嘛,质量还是有保证的。由于购买的电源的输出功率很大,它甚至可以用于测试设备的电流值。这样设备设计调试完成后,在可靠性测试阶段,它可以一直监控其电流值,方便测试其平均功耗,这样在设备注册时有数据支撑。

      依据厂家提供的说明书,来编写程序。说明书支持的设备型号如下:

程序的目标是:

(1)获取电脑的串口号,方便串口串口连接电源

(2)能够配置电流和电压,并有弹窗反馈

(3)能够读取电源实际输出的电流和电压,并绘制到折线图上

硬件部分:

a.电源的硬件:

(1)电源本体  (2)232转网线的转接线 (3)品字插头电源线

b.通讯方式  :232通讯  ,可以组菊花链通讯,如下图。

c.通讯协议:设备提供两种通讯协议,GEN 和SCPI 协议  ,为了兼容老的TDK设备,本次是用GEN这个通讯协议集。

d.设备的硬件配置,可以去TDK官网查看说明书。

Z10-20 : 详细信息 | 电源 - 可编程电源 | TDK 产品中心

程序部分:

(1)获取电脑的可用COM口      

def get_useful_serial_port(self):
    ports = []
    for port in serial.tools.list_ports.comports():
        ports.append(port.name)
self.custom_signal.emit(ports)

   使用到的工具是 serial.tools.list_ports.comports() ,可以获取电脑上的所有COM号。将获取到COM添加到列表里。使用emit 发射函数把获取到的端口号传递出去。(当然也可以不用发射函数,直接使用全局变量)

(2)串口连接函数

def serial_connect_and_disconect(self):

    # 下拉框的值获取,下拉框获取到值的类型为str,需要根据接口的类型要求进行类型转换
    # 串口号,需要str不用转化
    current_serial_port = self.combo_box_serial_port.currentText()
    # 波特率,需要int类型
    current_baud = int(self.combo_box_baud.currentText())
    # 数据位,需要 pyserial 常量,上面定义了字典,现在需用key值取出value值
    current_data_bits = self.data_bits_options[self.combo_box_data_bits.currentText()]
    # 校验位,需要 pyserial 常量,上面定义了字典,现在需用key值取出value值
    current_parity = self.parity_options[self.combo_box_parity.currentText()]
    # 停止位,需要 pyserial 常量,上面定义了字典,现在需用key值取出value值
    current_top_bits = self.stop_bits_options[self.combo_box_stop_bits.currentText()]
    '''
    #print(current_serial_port)
    #print(type(current_serial_port))
    '''
    # 获取按键的状态
    if self.btn_serial_port_connect.isChecked():
        # 按钮的字体
        self.btn_serial_port_connect.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # 按钮的外框
        self.btn_serial_port_connect.setStyleSheet(
            "border: 1px solid gray; border-radius: 10px;background-color: #FF0000;")
        try:
            self.serial_port = serial.Serial(
                port=current_serial_port,
                baudrate=current_baud,
                bytesize=current_data_bits,
                parity=current_parity,
                stopbits=current_top_bits,
                timeout=0.1
            )
            if self.serial_port.is_open:
                QMessageBox.information(None, "Information", "串口已打开", QMessageBox.Ok)
                # print(f"Connected to {current_serial_port} at {current_baud} baudrate.")
                # print(self.serial_port.is_open)
            return self.serial_port
        except serial.SerialException as e:
            QMessageBox.information(None, "Information", "连接失败:" + str(e), QMessageBox.Ok)
            # print(f"Failed to connect to {current_serial_port}: {current_baud}")
            return None
        # print("Button is checked")
    else:
        # 按钮的字体
        self.btn_serial_port_connect.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # 按钮的外框
        self.btn_serial_port_connect.setStyleSheet(
            "border: 1px solid gray; border-radius: 10px;background-color: #FFFFFF;")
        if self.serial_port and self.serial_port.is_open:
            self.serial_port.close()  # 关闭串口
            self.serial_port = None  # 清空串口对象
            self.statusBar().showMessage('未连接')  # 更新状态栏显示未连接
            QMessageBox.information(self, "Information", "串口已断开", QMessageBox.Ok)
        else:
            QMessageBox.information(self, "Information", "串口未连接", QMessageBox.Ok)
        # print("Button is not checked")

使用try来提高系统的稳定性。如果连接失败,会有弹窗提示。例如:

QMessageBox.information(None, "Information", "连接失败:" + str(e), QMessageBox.Ok)

编程思路:

   串口连接上后,手动获取端口号——>串口连接——>发送报文,获取电压、电流值——>根据获取的电压、电流值绘制折线图

整体程序

import sys
import serial
import random
import time
import serial.tools.list_ports
from collections import deque
from PySide6.QtCore import Signal, Slot
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QComboBox, QLabel, QMessageBox, QLineEdit, QWidget
from PySide6.QtCharts import QChartView, QChart, QLineSeries, QDateTimeAxis, QValueAxis, QSplineSeries
from PySide6.QtCore import QDateTime, QTimer, Qt
from PySide6.QtCore import Qt, QThread, Signal, Slot
from PySide6.QtGui import QPainter

# 创建一个线程
class WorkerThread(QThread):
    data_updated = Signal(float, float)
    voltage_value_updated = Signal(float, float)
    get_current_signal = Signal(float)

    def __init__(self):
        super().__init__()
        self.stopped = False

    def stop(self):
        self.stopped = True

    # QThread中规定 run为线程的入口。就像main是主函数的入口。
    # QThread中的start方法,会启用这个线程——>寻找run函数,作为入口——>开始执行

    def run(self):
        self.data_send_current()

    def data_send_current(self):
        self.stopped = False
        start_time = time.time()
        while not self.stopped:
            current_time = time.time() - start_time

            # random_value = random.randint(0, 18)
            if tdk_output_real_current == 0:
                random_value = 0
            else:
                random_value = tdk_output_real_current
            if tdk_output_real_voltage == 0:
                voltage_value = 0
            else:
                voltage_value = tdk_output_real_voltage

            self.data_updated.emit(current_time, random_value)
            self.voltage_value_updated.emit(current_time, voltage_value)
            time.sleep(0.1)  # 模拟数据更新间隔

# 基本框架
class MyWidow(QMainWindow):
    # 定义一个自定义信号,该信号会传递一个列表(数组)
    custom_signal = Signal(list)
    # 接受发射函数,发射的电流值
    output_current_signal = Signal(float)

    # print(f"获取到的电流{output_}")
    def __init__(self):
        super(MyWidow, self).__init__()
        self.serial_port = None  # 初始化串口对象为 None
        # 窗体
        # 确定MyWindow的大小
        self.resize(1000, 600)
        # 设置窗体不可缩放
        self.setFixedSize(self.size())
        # 设置窗口的名称
        self.setWindowTitle('TDK上位机')
        # 设置窗口透明度为0.5(半透明),0完全透明,1不透明
        self.setWindowOpacity(1)
        # self.setWindowIcon()
        # 创建一个图标
        self.chart = QChart()
        self.chart.setTitle("电流/压-时间图")
        # 电流曲线
        self.series = QSplineSeries()
        self.series.setName("电流值")
        self.chart.addSeries(self.series)
        # 电压曲线
        self.series2 = QSplineSeries()
        self.series2.setName("电压值")
        self.chart.addSeries(self.series2)

        self.axis_x = QValueAxis()
        self.axis_x.setTitleText("时间 (秒)")
        self.axis_x.setLabelFormat("%.1f")
        self.axis_x.setTickCount(10)
        self.chart.addAxis(self.axis_x, Qt.AlignBottom)
        self.series.attachAxis(self.axis_x)
        self.series2.attachAxis(self.axis_x)

        self.axis_y = QValueAxis()
        self.axis_y.setTitleText("电流/压值")
        self.axis_y.setLabelFormat("%i")
        # 生成一个列表,0-20,补偿为2,即元素为0,2,4,6,8等
        ticks = list(range(0, 42, 4))
        # print(ticks)
        # ticks = list(range(0, 2,1))
        # 将列表内元素的数量设为,Y轴的刻度数量,ticks有11,所以有11个刻度
        self.axis_y.setTickCount(len(ticks))
        # 设置y轴值域,最大值为20,最小值为0
        self.axis_y.setRange(0, 40)
        # self.axis_y.setRange(0, 2)
        self.chart.addAxis(self.axis_y, Qt.AlignLeft)
        self.series.attachAxis(self.axis_y)
        self.series2.attachAxis(self.axis_y)

        self.chart_view = QChartView(self.chart)
        self.chart_view.setRenderHint(QPainter.Antialiasing)

        # 创建一个 QWidget 容器
        self.container = QWidget(self)
        self.container.setGeometry(460, 20, 600, 900)  # 设置容器的位置和大小

        # 将 chart_view 添加到容器中
        self.chart_view.setGeometry(5, 5, 540, 500)  # 设置 chart_view 的位置和大小
        self.chart_view.setParent(self.container)

        # 使用 deque 来维护固定长度的队列
        # 电流队列
        self.data_queue = deque(maxlen=100)
        # 电压队列
        self.data_queue2 = deque(maxlen=100)

        # 添加一个按钮来控制折线图的启用和禁用
        self.toggle_button = QPushButton("开始采样", self)
        self.toggle_button.setGeometry(820, 530, 120, 40)
        self.toggle_button.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.toggle_button.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.toggle_button.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")

        # 设置采样按钮为切换按钮
        self.toggle_button.setCheckable(True)
        self.toggle_button.toggled.connect(self.toggle_chart)
        self.toggle_button.toggled.connect(self.print_t)
        self.chart_enabled = False
        self.worker_thread = None

        # 按钮

        # 新建一个按钮,用于获取PC可用的串口号
        self.btn_get = QPushButton('获取', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_get.clicked.connect(self.get_useful_serial_port)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_get.setGeometry(180, 40, 60, 40)
        self.btn_get.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_get.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        #######################################################################################
        # 发射函数的信号,将信号在连接到一个槽函数,这个槽函数是更新label的标签
        self.custom_signal.connect(self.on_custom_signal)
        #######################################################################################
        # 串口连接按钮
        self.btn_serial_port_connect = QPushButton('连接', self)
        # 设置按钮位切换按钮(带自锁的按钮),每次按下都会对自身状态取反
        self.btn_serial_port_connect.setCheckable(True)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_serial_port_connect.clicked.connect(self.serial_connect_and_disconect)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_serial_port_connect.setGeometry(30, 330, 200, 40)
        # 按钮的字体
        self.btn_serial_port_connect.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # 按钮的外框
        self.btn_serial_port_connect.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        # 按钮的内部填充颜色
        self.btn_serial_port_connect.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")
        # 地址应用
        self.btn_addr_enter = QPushButton('查询', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_addr_enter.clicked.connect(self.check_addr)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_addr_enter.setGeometry(400, 40, 60, 40)
        self.btn_addr_enter.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_addr_enter.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.btn_addr_enter.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")
        # 电压值设置
        self.btn_set_Voltage = QPushButton('应用', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_set_Voltage.clicked.connect(self.set_Voltage)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_set_Voltage.setGeometry(400, 90, 60, 40)
        self.btn_set_Voltage.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_set_Voltage.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.btn_set_Voltage.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")
        # 电流值设置
        self.btn_set_Current = QPushButton('应用', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_set_Current.clicked.connect(self.set_Current)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_set_Current.setGeometry(400, 140, 60, 40)
        self.btn_set_Current.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_set_Current.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.btn_set_Current.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")
        # 读取设置的电压值
        self.btn_read_Voltage = QPushButton('读取', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_read_Voltage.clicked.connect(self.read_Voltage)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_read_Voltage.setGeometry(290, 190, 60, 40)
        self.btn_read_Voltage.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_read_Voltage.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.btn_read_Voltage.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")
        # 读取设置的电流值
        self.btn_read_Current = QPushButton('读取', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_read_Current.clicked.connect(self.read_Current)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_read_Current.setGeometry(290, 240, 60, 40)
        self.btn_read_Current.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_read_Current.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.btn_read_Current.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")
        # 读取输出的电压值
        self.btn_read_real_Voltage = QPushButton('读取', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_read_real_Voltage.clicked.connect(self.read_real_Voltage)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_read_real_Voltage.setGeometry(290, 290, 60, 40)
        self.btn_read_real_Voltage.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_read_real_Voltage.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.btn_read_real_Voltage.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")
        # 读取输出的电流值
        self.btn_read_real_Current = QPushButton('读取', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_read_real_Current.clicked.connect(self.read_real_Current)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_read_real_Current.setGeometry(290, 340, 60, 40)
        self.btn_read_real_Current.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_read_real_Current.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.btn_read_real_Current.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")
        # 开启输出
        self.btn_mode_on = QPushButton('开启输出', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_mode_on.clicked.connect(self.out_mode_on)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_mode_on.setGeometry(500, 530, 120, 40)
        self.btn_mode_on.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_mode_on.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.btn_mode_on.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")
        # 开启输出
        self.btn_mode_off = QPushButton('关闭输出', self)
        # 把按钮用点击事件链接到槽函数上,这样可以做触发
        self.btn_mode_off.clicked.connect(self.out_mode_off)
        # 设置按钮的位置和大小,设置左上角坐标为(20, 100),按钮的宽度为0,高度为50
        self.btn_mode_off.setGeometry(660, 530, 120, 40)
        self.btn_mode_off.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.btn_mode_off.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.btn_mode_off.setStyleSheet(
            "QPushButton { font-family: Arial; font-size: 12pt; font-weight: bold; border: 1px solid gray; border-radius: 10px; background-color: #f0f0f0; } QPushButton:hover { background-color: #D9E3F8; } QPushButton:pressed { background-color: #E0E0E0; }")

        # 标签
        self.text_label = QLabel('波特率', self)
        self.text_label.setGeometry(20, 90, 60, 40)
        self.text_label.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.text_label2 = QLabel('校验位', self)
        self.text_label2.setGeometry(20, 150, 60, 40)
        self.text_label2.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.text_label2 = QLabel('数据位', self)
        self.text_label2.setGeometry(20, 210, 60, 40)
        self.text_label2.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.text_label2 = QLabel('停止位', self)
        self.text_label2.setGeometry(20, 270, 60, 40)
        self.text_label2.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # 电压显示
        # 在下面回给label赋值
        self.text_label_Voltage = QLabel(self)
        self.text_label_Voltage.setGeometry(360, 190, 100, 40)
        self.text_label_Voltage.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.text_label_Voltage.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        # 电流显示
        self.text_label_Current = QLabel(self)
        self.text_label_Current.setGeometry(360, 240, 100, 40)
        self.text_label_Current.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.text_label_Current.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        # 实际输出电压显示
        self.text_label_real_Voltage = QLabel(self)
        self.text_label_real_Voltage.setGeometry(360, 290, 100, 40)
        self.text_label_real_Voltage.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.text_label_real_Voltage.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        # 实际输出电流显示
        self.text_label_real_Current = QLabel(self)
        self.text_label_real_Current.setGeometry(360, 340, 100, 40)
        self.text_label_real_Current.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        self.text_label_real_Current.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        # 输入框
        # 地址
        self.text_edit_addr = QLineEdit(self)
        # 设置为placeholderText(文本占位符),当用户输入时预设文本自动消失
        self.text_edit_addr.setPlaceholderText("地址")
        # 设置左上角坐标为(x, y),输入框的宽度和高度
        self.text_edit_addr.setGeometry(290, 40, 100, 40)  # 设置输入框的位置和大小
        # 设置QLineEdit的字体和字号
        self.text_edit_addr.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # 设置QLineEdit的外框的线宽和圆角样式
        self.text_edit_addr.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        # 电压值设置
        self.text_edit_Voltage = QLineEdit(self)
        # 设置为placeholderText(文本占位符),当用户输入时预设文本自动消失
        self.text_edit_Voltage.setPlaceholderText("电压值")
        # 设置左上角坐标为(x, y),输入框的宽度和高度
        self.text_edit_Voltage.setGeometry(290, 90, 100, 40)  # 设置输入框的位置和大小
        # 设置QLineEdit的字体和字号
        self.text_edit_Voltage.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # 设置QLineEdit的外框的线宽和圆角样式
        self.text_edit_Voltage.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        # 电流值设置
        self.text_edit_Current = QLineEdit(self)
        # 设置为placeholderText(文本占位符),当用户输入时预设文本自动消失
        self.text_edit_Current.setPlaceholderText("电流值")
        # 设置左上角坐标为(x, y),输入框的宽度和高度
        self.text_edit_Current.setGeometry(290, 140, 100, 40)  # 设置输入框的位置和大小
        # 设置QLineEdit的字体和字号
        self.text_edit_Current.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # 设置QLineEdit的外框的线宽和圆角样式
        self.text_edit_Current.setStyleSheet("border: 1px solid gray; border-radius: 10px;")

        # 下拉框
        # 串口选择
        self.combo_box_serial_port = QComboBox(self)
        self.combo_box_serial_port.setGeometry(20, 40, 150, 40)
        self.combo_box_serial_port.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # self.combo_box_serial_port.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.combo_box_serial_port.setStyleSheet("border: 1px solid gray")
        # 波特率选择
        baud_array = [9600, 11400, 19200, 38400, 115200]
        self.combo_box_baud = QComboBox(self)
        # 将整数转换为字符串并添加到下拉框
        self.combo_box_baud.addItems(map(str, baud_array))
        self.combo_box_baud.setGeometry(90, 90, 150, 40)
        self.combo_box_baud.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # self.combo_box_serial_port.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.combo_box_baud.setStyleSheet("border: 1px solid gray")
        # 校验位选择
        self.parity_options = {
            "None": serial.PARITY_NONE,
            "Odd": serial.PARITY_ODD,
            "Even": serial.PARITY_EVEN,
            "Mark": serial.PARITY_MARK,
            "Space": serial.PARITY_SPACE
        }
        self.combo_box_parity = QComboBox(self)
        # 将整数转换为字符串并添加到下拉框
        self.combo_box_parity.addItems(self.parity_options.keys())
        self.combo_box_parity.setGeometry(90, 150, 150, 40)
        self.combo_box_parity.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # self.combo_box_serial_port.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.combo_box_parity.setStyleSheet("border: 1px solid gray")

        # 数据位
        self.data_bits_options = {
            "8 bits": serial.EIGHTBITS,
            "7 bits": serial.SEVENBITS,
            "6 bits": serial.SIXBITS,
            "5 bits": serial.FIVEBITS
        }
        self.combo_box_data_bits = QComboBox(self)
        # 将整数转换为字符串并添加到下拉框
        self.combo_box_data_bits.addItems(self.data_bits_options.keys())
        self.combo_box_data_bits.setGeometry(90, 210, 150, 40)
        self.combo_box_data_bits.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # self.combo_box_serial_port.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.combo_box_data_bits.setStyleSheet("border: 1px solid gray")

        # 停止位
        self.stop_bits_options = {
            "1 bit": serial.STOPBITS_ONE,
            "1.5 bits": serial.STOPBITS_ONE_POINT_FIVE,
            "2 bits": serial.STOPBITS_TWO
        }
        self.combo_box_stop_bits = QComboBox(self)
        # 将整数转换为字符串并添加到下拉框
        self.combo_box_stop_bits.addItems(self.stop_bits_options.keys())
        self.combo_box_stop_bits.setGeometry(90, 270, 150, 40)
        self.combo_box_stop_bits.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
        # self.combo_box_serial_port.setStyleSheet("border: 1px solid gray; border-radius: 10px;")
        self.combo_box_stop_bits.setStyleSheet("border: 1px solid gray")

        ######
        self.output_current_signal.connect(self.update_current_value)
        #####

    def on_custom_signal(self, ports):

        self.combo_box_serial_port.clear()
        for item in ports:
            self.combo_box_serial_port.addItem(str(item))
        # print(ports)

    def get_useful_serial_port(self):
        ports = []
        for port in serial.tools.list_ports.comports():
            ports.append(port.name)
        # 发射函数,因为信号与槽之间是无法传递参数的,只能使用发射函数
        # 流程是用emit属性,把custom_signal当做信号,连接on_custom_signal上,同时把参数传递过去。一个按钮触发两个槽函数
        # 流程是btn_get(信号)->get_useful_serial_port(槽)—>(自动)custom_signal(信号)—>on_custom_signal(槽)
        self.custom_signal.emit(ports)
        # print(ports)

    def serial_connect_and_disconect(self):

        # 下拉框的值获取,下拉框获取到值的类型为str,需要根据接口的类型要求进行类型转换
        # 串口号,需要str不用转化
        current_serial_port = self.combo_box_serial_port.currentText()
        # 波特率,需要int类型
        current_baud = int(self.combo_box_baud.currentText())
        # 数据位,需要 pyserial 常量,上面定义了字典,现在需用key值取出value值
        current_data_bits = self.data_bits_options[self.combo_box_data_bits.currentText()]
        # 校验位,需要 pyserial 常量,上面定义了字典,现在需用key值取出value值
        current_parity = self.parity_options[self.combo_box_parity.currentText()]
        # 停止位,需要 pyserial 常量,上面定义了字典,现在需用key值取出value值
        current_top_bits = self.stop_bits_options[self.combo_box_stop_bits.currentText()]
        '''
        #print(current_serial_port)
        #print(type(current_serial_port))
        '''
        # 获取按键的状态
        if self.btn_serial_port_connect.isChecked():
            # 按钮的字体
            self.btn_serial_port_connect.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
            # 按钮的外框
            self.btn_serial_port_connect.setStyleSheet(
                "border: 1px solid gray; border-radius: 10px;background-color: #FF0000;")
            try:
                self.serial_port = serial.Serial(
                    port=current_serial_port,
                    baudrate=current_baud,
                    bytesize=current_data_bits,
                    parity=current_parity,
                    stopbits=current_top_bits,
                    timeout=0.1
                )
                if self.serial_port.is_open:
                    QMessageBox.information(None, "Information", "串口已打开", QMessageBox.Ok)
                    # print(f"Connected to {current_serial_port} at {current_baud} baudrate.")
                    # print(self.serial_port.is_open)
                return self.serial_port
            except serial.SerialException as e:
                QMessageBox.information(None, "Information", "连接失败:" + str(e), QMessageBox.Ok)
                # print(f"Failed to connect to {current_serial_port}: {current_baud}")
                return None
            # print("Button is checked")
        else:
            # 按钮的字体
            self.btn_serial_port_connect.setStyleSheet("font-family: Arial; font-size: 12pt; font-weight: bold;")
            # 按钮的外框
            self.btn_serial_port_connect.setStyleSheet(
                "border: 1px solid gray; border-radius: 10px;background-color: #FFFFFF;")
            if self.serial_port and self.serial_port.is_open:
                self.serial_port.close()  # 关闭串口
                self.serial_port = None  # 清空串口对象
                self.statusBar().showMessage('未连接')  # 更新状态栏显示未连接
                QMessageBox.information(self, "Information", "串口已断开", QMessageBox.Ok)
            else:
                QMessageBox.information(self, "Information", "串口未连接", QMessageBox.Ok)
            # print("Button is not checked")

    # 查询设备
    def check_addr(self):
        addr = self.text_edit_addr.text()
        # 拼接报文
        ascii_code = "ADR " + addr + "\r\n"
        # print('Sending:', ascii_code)

        if self.serial_port and self.serial_port.is_open:
            try:
                # Send ASCII code
                self.serial_port.write(ascii_code.encode('ascii'))
                # print(f"Sent ASCII code: {ascii_code}")

                # Attempt to receive data
                received_data = self.serial_port.read(100)  # Read up to 100 bytes
                if received_data:
                    # 加.strip()防止返回的有空格
                    received_text = received_data.decode('ascii').strip()
                    # print(f"Received data: {received_text}")
                    # print(type(received_text))
                    if received_text == "OK":
                        QMessageBox.information(None, "Information", "查询成功", QMessageBox.Ok)
                    elif received_text != "OK":
                        self.error_code(received_text)
                    # Process received data here, update GUI, etc.
                else:
                    QMessageBox.information(None, "Information", "无数据返回", QMessageBox.Ok)
                    # print("No data received.")

            except Exception as e:
                QMessageBox.information(None, "Information", f"发送或接收失败:{str(e)}", QMessageBox.Ok)
                # print(f"Failed to send or receive: {str(e)}")

        else:
            QMessageBox.information(None, "Information", "请先连接串口", QMessageBox.Ok)
            # print("Serial port not connected")

    # 设置电压值

    def set_Voltage(self):
        Voltage = self.text_edit_Voltage.text()
        # 拼接报文
        ascii_code = "PV " + Voltage + "\r\n"
        # print('Sending:', ascii_code)

        if self.serial_port and self.serial_port.is_open:
            try:
                # Send ASCII code
                self.serial_port.write(ascii_code.encode('ascii'))
                # print(f"Sent ASCII code: {ascii_code}")

                # Attempt to receive data
                received_data = self.serial_port.read(100)  # Read up to 100 bytes
                if received_data:
                    # 加.strip()防止返回的有空格
                    received_text = received_data.decode('ascii').strip()
                    # print(f"Received data: {received_text}")
                    # print(type(received_text))
                    if received_text == "OK":
                        QMessageBox.information(None, "Information", "电压设置成功", QMessageBox.Ok)
                    elif received_text != "OK":
                        self.error_code(received_text)
                    # Process received data here, update GUI, etc.
                else:
                    QMessageBox.information(None, "Information", "无数据返回", QMessageBox.Ok)
                    # print("No data received.")

            except Exception as e:
                QMessageBox.information(None, "Information", f"发送或接收失败:{str(e)}", QMessageBox.Ok)
                # print(f"Failed to send or receive: {str(e)}")

        else:
            QMessageBox.information(None, "Information", "请先连接串口", QMessageBox.Ok)
            # print("Serial port not connected")

    # 设置电流值
    def set_Current(self):
        Current = self.text_edit_Current.text()
        # 拼接报文
        ascii_code = "PC " + Current + "\r\n"
        # print('Sending:', ascii_code)

        if self.serial_port and self.serial_port.is_open:
            try:
                # Send ASCII code
                self.serial_port.write(ascii_code.encode('ascii'))
                # print(f"Sent ASCII code: {ascii_code}")

                # Attempt to receive data
                received_data = self.serial_port.read(100)  # Read up to 100 bytes
                if received_data:
                    # 加.strip()防止返回的有空格
                    received_text = received_data.decode('ascii').strip()
                    # print(f"Received data: {received_text}")
                    # print(type(received_text))
                    if received_text == "OK":
                        QMessageBox.information(None, "Information", "电流设置成功", QMessageBox.Ok)
                    elif received_text != "OK":
                        self.error_code(received_text)
                    # Process received data here, update GUI, etc.
                else:
                    QMessageBox.information(None, "Information", "无数据返回", QMessageBox.Ok)

            except Exception as e:
                QMessageBox.information(None, "Information", f"发送或接收失败:{str(e)}", QMessageBox.Ok)
                # print(f"Failed to send or receive: {str(e)}")

        else:
            QMessageBox.information(None, "Information", "请先连接串口", QMessageBox.Ok)
            # print("Serial port not connected")

    # 读取电压值
    def read_Voltage(self):
        ascii_code = "PV?\r\n"
        # print('Sending:', ascii_code)

        if self.serial_port and self.serial_port.is_open:
            try:
                # Send ASCII code
                self.serial_port.write(ascii_code.encode('ascii'))
                # print(f"Sent ASCII code: {ascii_code}")

                # Attempt to receive data
                received_data = self.serial_port.read(100)  # Read up to 100 bytes
                if received_data:
                    # 加.strip()防止返回的有空格
                    received_text = received_data.decode('ascii').strip()
                    self.text_label_Voltage.setText(f"设置电压:{received_text}")
                    # print(f"Received data: {received_text}")
                # Process received data here, update GUI, etc.
                else:
                    QMessageBox.information(None, "Information", "无数据返回", QMessageBox.Ok)
                    # print("No data received.")

            except Exception as e:
                QMessageBox.information(None, "Information", f"发送或接收失败:{str(e)}", QMessageBox.Ok)
                # print(f"Failed to send or receive: {str(e)}")

        else:
            QMessageBox.information(None, "Information", "请先连接串口", QMessageBox.Ok)
            # print("Serial port not connected")

        # 读取电流值

    def read_Current(self):
        ascii_code = "PC?\r\n"
        # print('Sending:', ascii_code)

        if self.serial_port and self.serial_port.is_open:
            try:
                # Send ASCII code
                self.serial_port.write(ascii_code.encode('ascii'))
                # print(f"Sent ASCII code: {ascii_code}")
                # Attempt to receive data
                received_data = self.serial_port.read(100)  # Read up to 100 bytes
                if received_data:
                    # 加.strip()防止返回的有空格
                    received_text = received_data.decode('ascii').strip()
                    self.text_label_Current.setText(f"设置电流:{received_text}")
                    # print(f"Received data: {received_text}")
                # Process received data here, update GUI, etc.
                else:
                    QMessageBox.information(None, "Information", "无数据返回", QMessageBox.Ok)
                    # print("No data received.")
            except Exception as e:
                QMessageBox.information(None, "Information", f"发送或接收失败:{str(e)}", QMessageBox.Ok)
                # print(f"Failed to send or receive: {str(e)}")

        else:
            QMessageBox.information(None, "Information", "请先连接串口", QMessageBox.Ok)
            # print("Serial port not connected")

        # 读取电压值

    def read_real_Voltage(self):
        ascii_code = "MV?\r\n"
        global tdk_output_real_voltage
        # print('Sending:', ascii_code)
        if self.serial_port and self.serial_port.is_open:
            try:
                # Send ASCII code
                self.serial_port.write(ascii_code.encode('ascii'))
                # print(f"Sent ASCII code: {ascii_code}")

                # Attempt to receive data
                received_data = self.serial_port.read(100)  # Read up to 100 bytes
                if received_data:
                    # 加.strip()防止返回的有空格
                    received_text = received_data.decode('ascii').strip()
                    output_voltage = float(received_text)
                    # 将获取到的电流值赋值给全局变量
                    tdk_output_real_voltage = output_voltage
                    self.text_label_real_Voltage.setText(f"输出电压:{received_text}")
                    # print(f"Received data: {received_text}")
                # Process received data here, update GUI, etc.
                else:
                    QMessageBox.information(None, "Information", "无数据返回", QMessageBox.Ok)
                    # print("No data received.")

            except Exception as e:
                QMessageBox.information(None, "Information", f"发送或接收失败:{str(e)}", QMessageBox.Ok)
                # print(f"Failed to send or receive: {str(e)}")

        else:
            # QMessageBox.information(None, "Information", "请先连接串口", QMessageBox.Ok)
            pass
            # print("Serial port not connected")

        # 读取电流值

    def read_real_Current(self):
        # 定义一个全局变量,全局变量可以不同的类和槽函数之间共享
        global tdk_output_real_current
        ascii_code = "MC?\r\n"
        # print('Sending:', ascii_code)

        if self.serial_port and self.serial_port.is_open:
            try:
                # Send ASCII code
                self.serial_port.write(ascii_code.encode('ascii'))
                # print(f"Sent ASCII code: {ascii_code}")
                # Attempt to receive data
                received_data = self.serial_port.read(100)  # Read up to 100 bytes
                if received_data:
                    # 加.strip()防止返回的有空格
                    received_text = received_data.decode('ascii').strip()
                    self.text_label_real_Current.setText(f"输出电流:{received_text}")
                    output_current = float(received_text)
                    # 将获取到的电流值赋值给全局变量
                    tdk_output_real_current = output_current
                    self.output_current_signal.emit(output_current)
                # Process received data here, update GUI, etc.
                else:
                    QMessageBox.information(None, "Information", "无数据返回", QMessageBox.Ok)
                    # print("No data received.")
            except Exception as e:
                QMessageBox.information(None, "Information", f"发送或接收失败:{str(e)}", QMessageBox.Ok)
            # print(f"Failed to send or receive: {str(e)}")

        else:
            # QMessageBox.information(None, "Information", "请先连接串口", QMessageBox.Ok)
            pass
            # print("Serial port not connected")

    def out_mode_on(self):

        ascii_code = "OUT ON\r\n"

        if self.serial_port and self.serial_port.is_open:
            try:
                # Send ASCII code
                self.serial_port.write(ascii_code.encode('ascii'))
                # print(f"Sent ASCII code: {ascii_code}")

                # Attempt to receive data
                received_data = self.serial_port.read(100)  # Read up to 100 bytes
                if received_data:
                    # 加.strip()防止返回的有空格
                    received_text = received_data.decode('ascii').strip()
                    # print(f"Received data: {received_text}")
                    # print(type(received_text))
                    if received_text == "OK":
                        QMessageBox.information(None, "Information", "开启输出", QMessageBox.Ok)
                    elif received_text != "OK":
                        self.error_code(received_text)
                    # Process received data here, update GUI, etc.
                else:
                    QMessageBox.information(None, "Information", "无数据返回", QMessageBox.Ok)

            except Exception as e:
                QMessageBox.information(None, "Information", f"发送或接收失败:{str(e)}", QMessageBox.Ok)

    def out_mode_off(self):

        ascii_code = "OUT OFF\r\n"

        if self.serial_port and self.serial_port.is_open:
            try:
                # Send ASCII code
                self.serial_port.write(ascii_code.encode('ascii'))
                # print(f"Sent ASCII code: {ascii_code}")

                # Attempt to receive data
                received_data = self.serial_port.read(100)  # Read up to 100 bytes
                if received_data:
                    # 加.strip()防止返回的有空格
                    received_text = received_data.decode('ascii').strip()
                    # print(f"Received data: {received_text}")
                    # print(type(received_text))
                    if received_text == "OK":
                        QMessageBox.information(None, "Information", "关闭输出", QMessageBox.Ok)
                    elif received_text != "OK":
                        self.error_code(received_text)
                    # Process received data here, update GUI, etc.
                else:
                    QMessageBox.information(None, "Information", "无数据返回", QMessageBox.Ok)
            except Exception as e:
                QMessageBox.information(None, "Information", f"发送或接收失败:{str(e)}", QMessageBox.Ok)

    def error_code(self, text):
        if text == "E01":
            QMessageBox.information(None, "Information", "E01:编程电压 (PV) 高于可接受范围", QMessageBox.Ok)
        elif text == "E02":
            QMessageBox.information(None, "Information", "E02:输出电压设定值低于 UVL 设定值", QMessageBox.Ok)
        elif text == "E04":
            QMessageBox.information(None, "Information", "E04:OVP 设定值低于可接受范围", QMessageBox.Ok)
        elif text == "E06":
            QMessageBox.information(None, "Information", "E06:UVL 设定值高于输出电压设定值", QMessageBox.Ok)
        elif text == "E07":
            QMessageBox.information(None, "Information", "E07:电源因锁定故障关闭", QMessageBox.Ok)
        elif text == "E08":
            QMessageBox.information(None, "Information", "E08:电源处于高级并联模式,无法执行该命令", QMessageBox.Ok)
        elif text == "C01":
            QMessageBox.information(None, "Information", "C01:非法命令或查询", QMessageBox.Ok)
        elif text == "C02":
            QMessageBox.information(None, "Information", "C02:缺少参数", QMessageBox.Ok)
        elif text == "C03":
            QMessageBox.information(None, "Information", "C03:非法参数", QMessageBox.Ok)
        elif text == "C04":
            QMessageBox.information(None, "Information", "C04:校验和错误", QMessageBox.Ok)
        elif text == "C04":
            QMessageBox.information(None, "Information", "C05:设定值超出范围", QMessageBox.Ok)

    # 切换按钮
    def toggle_chart(self, checked):
        # 初始按钮设置被按下
        self.chart_enabled = checked
        # 如果按钮被按下
        if self.chart_enabled:
            self.toggle_button.setText("禁用折线图")
            self.restart_worker_thread()
            # print("checked")
        # 如果按钮没有被按下
        else:
            self.toggle_button.setText("启用折线图")
            self.stop_worker_thread()
            self.clear_chart()
            # print("not checked")
        # 互锁,连接按钮为False时,要强制把采样按钮置为False

    # 重新启用线程
    def restart_worker_thread(self):
        # 停止当前工作线程
        self.stop_worker_thread()
        # 创建新的工作线程,(实例化上面的一个类,类里面创了线程。self.worker_thread继承了WorkerThread()里的方法)
        self.worker_thread = WorkerThread()
        # 更新数据
        self.worker_thread.data_updated.connect(self.update_chart)
        # 电压值
        self.worker_thread.voltage_value_updated.connect(self.update_chart2)
        # 开始运行工作线程
        self.worker_thread.start()

    def stop_worker_thread(self):
        if self.worker_thread is not None:
            self.worker_thread.stop()
            self.worker_thread.wait()
            self.worker_thread = None

    # 清空折线图
    def clear_chart(self):
        # 清除数据点
        self.data_queue.clear()
        # 清除数据序列
        self.series.clear()

    # 更新数据点
    def update_chart(self, current_time, random_value):
        # 如果按钮被按下
        if self.chart_enabled:
            # 添加数据点,X和Y轴的数据点
            self.data_queue.append((current_time, random_value))
            # 清空数据序列
            self.series.clear()
            # 遍历列表
            for data_point in self.data_queue:
                self.series.append(data_point[0], data_point[1])
            # 每次超过10,就重置X轴的标注
            if current_time > 10:
                self.axis_x.setRange(current_time - 10, current_time)

    # 更新数据点
    def update_chart2(self, current_time, random_value):
        # 如果按钮被按下
        if self.chart_enabled:
            # 添加数据点,X和Y轴的数据点
            self.data_queue2.append((current_time, random_value))
            # 清空数据序列
            self.series2.clear()
            # 遍历列表
            for data_point in self.data_queue2:
                self.series2.append(data_point[0], data_point[1])
            # 每次超过10,就重置X轴的标注
            if current_time > 10:
                self.axis_x.setRange(current_time - 10, current_time)

    #
    def update_current_value(self, value):
        #print(value)
        # print(f"全局变量为:{tdk_output_real_current}")
        pass

    def print_t(self):
        # print("ok")
        self.timer = QTimer()
        self.timer.timeout.connect(self.read_real_Current)
        self.timer.timeout.connect(self.read_real_Voltage)
        self.timer.start(100)

if __name__ == '__main__':
    app = QApplication([])
    win = MyWidow()
    win.show()
    sys.exit(app.exec())
    # 基本框架

程序运行效果

程序有些臃肿,有很多可以优化的地方,但是还是够用的,所以还好。还有界面美化的部分,本人的审美能力有限,所以暂时不做美化处理了。

关于线程和绘制折线图的那部分程序的讲解,有时间我会单开一个文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值