当前位置: 首页 > news >正文

QT开发--串口通信

第十六章 串口通信

16.1 串口通信基础

串口通信主要通过DB9接口,适用于短距离(<10米)。关键参数包括:

  • 波特率:每秒传输bit数,如9600。
  • 数据位:信息包中的有效数据位数。
  • 停止位:单个包的最后一位,通常为1或2位。
  • 奇偶校验位:检错方式,分偶、奇校验。

16.1.1 QtSerialPort模块简介

        QtSerialPort是QT5的附加模块,为硬件和虚拟串口提供接口。它简化串口应用开发,提供配置、I/O操作等功能。

        使用QtSerialPort,需包含以下头文件:

#include <QSerialPortInfo>  
#include <QSerialPort>

        并在.pro文件中添加:

QT += serialport

        QSerialPort类用于访问串口

        QSerialPortInfo用于获取串口信息

        可通过setPort()setPortName()设置要访问的串口

        使用open()close()打开和关闭串口

        串口打开后,可使用setBaudRate()等函数重新配置。

        waitForReadyRead()waitForBytesWritten()用于阻塞操作。

16.1.2 QSerialPort 成员函数

构造函数:

// 构造一个未初始化的QSerialPort对象,可选指定父对象  
QSerialPort::QSerialPort(QObject *parent = nullptr)  // 构造并初始化QSerialPort对象,指定端口名和可选的父对象  
QSerialPort::QSerialPort(const QString &name, QObject *parent = nullptr)  // 构造并初始化QSerialPort对象,根据QSerialPortInfo指定端口信息和可选的父对象  
QSerialPort::QSerialPort(const QSerialPortInfo &serialPortInfo, QObject *parent = nullptr)

关键方法:

        bool atEnd() const:检查是否有数据可读

        qint64 bytesAvailable() const:返回可读数据字节数

        qint64 bytesToWrite() const:返回可写数据字节数

        void close():关闭串口。

        void setPort(const QSerialPortInfo &serialPortInfo):设置串口信息

        void setPortName(const QString &name):设置串口名

        //波特率改变时触发信号

        [signal] void baudRateChanged(qint32 baudRate, //新的波特率
                        QSerialPort::Directions directions)          //波特率用于哪方

/*检查是否有数据可读,无数据可读返回true*/
[virtual] bool QSerialPort::atEnd() const;/*波特率改变后,信号触发*/
[signal] void QSerialPort::baudRateChanged(qint32 baudRate, //新的波特率QSerialPort::Directions directions) //波特率用于哪方
/*
QSerialPort::AllDirections //默认,表示读写方向都应用此波特率
QSerialPort::Input         //仅用于输入方向
QSerialPort::Output        //仅用于输出方向
*//*返回可读数据的字节数*/
[virtual] qint64 QSerialPort::bytesAvailable() const;/*返回可写数据的字节数*/
[virtual] qint64 QSerialPort::bytesToWrite() const;/*关闭串口*/
[virtual] void QSerialPort::close();/*设置串口端口信息为 serialPortInfo*/
void QSerialPort::setPort(const QSerialPortInfo &serialPortInfo);/*设置串口名为name*/
void QSerialPort::setPortName(const QString &name);

16.2 QSerialPortInfo

16.2.1 QSerialPortInfo 简介

        QSerialPortInfo 类用于提供系统中已有串口设备的信息。可通过其静态成员函数获取代表各串口的 QSerialPortInfo 对象链表。每个对象包含端口的详细信息,如端口名、系统位置、描述、制造商等,并可用于配置 QSerialPort 对象

16.2.2 QSerialPortInfo 成员函数

构造函数

QSerialPortInfo(const QSerialPort &port);  
QSerialPortInfo(const QString &name);  
QSerialPortInfo(const QSerialPortInfo &other);

静态成员函数

static QList<QSerialPortInfo> availablePorts(); // 返回可用串口链表  
static QList<qint32> standardBaudRates();      // 返回标准波特率链表

成员函数

QString description() const;         // 返回串口描述  
bool hasProductIdentifier() const; // 是否有生产编码  
bool hasVendorIdentifier() const; // 是否有制造商编码  
bool isBusy() const;               // 串口是否正忙  
QString manufacturer() const;     // 返回制造商名称  
QString portName() const;         // 返回串口名称  
quint16 productIdentifier() const; // 返回生产编码  
QString serialNumber() const;    // 返回序列号  
QString systemLocation() const; // 返回系统位置  
quint16 vendorIdentifier() const; // 返回制造商编码  
void swap(QSerialPortInfo &other); // 交换两个对象

16.3 实现简易串口

打开/关闭串口:

        当点击“打开串口”按钮时,配置串口参数并尝试打开串口。如果成功,禁用串口配置ComboBox,启用发送按钮,并更改按钮文本为“关闭串口”。

        同时连接串口的 readyRead信号到 readData槽函数。

        当点击“关闭串口”按钮时(即串口已打开状态),关闭串口,重新启用串口配置ComboBox,更改按钮文本为“打开串口”,并禁用发送按钮。

数据发送与接收:

        点击发送按钮时,将发送文本框中的内容以Latin1编码写入串口。

        点击清空接收/发送数据按钮时,清空相应的文本框内容。

        当串口有数据可读时,readData槽函数被调用,读取所有可用数据,并将其追加到接收文本框中。

#ifndef MAINWINDOW_H // 防止头文件重复包含,如果已定义MAINWINDOW_H则不再次包含  
#define MAINWINDOW_H  // 包含必要的Qt库头文件  
#include <QMainWindow> // 主窗口类  
#include <QSerialPort> // 串口通信类  
#include <QSerialPortInfo> // 串口信息类,用于获取系统中可用的串口信息  
#include <QList> // 用于存储串口信息列表  
#include <QDebug> // 用于调试输出  // Ui命名空间,包含UI类的声明(通常由Qt Designer生成)  
namespace Ui {  
class MainWindow;  
}  // MainWindow类,继承自QMainWindow  
class MainWindow : public QMainWindow {  Q_OBJECT // 使用Qt的宏,允许该类使用信号和槽机制  public:  // 构造函数,explicit关键字防止隐式转换  explicit MainWindow(QWidget *parent = 0);   // 析构函数  ~MainWindow();  private slots:  // 私有槽函数,用于处理UI按钮点击事件  void on_btn_openConsole_clicked(); // 打开控制台按钮点击事件处理  void on_btn_send_clicked(); // 发送按钮点击事件处理  void on_btn_clearRecv_clicked(); // 清空接收区按钮点击事件处理  void on_btn_clearSend_clicked(); // 清空发送区按钮点击事件处理  void readData(); // 读取串口数据  private:  // Ui类的指针,用于访问UI元素  Ui::MainWindow *ui;   // QSerialPort类的指针,用于串口通信  QSerialPort *serial;  
};  #endif // MAINWINDOW_H // 结束头文件保护
#include "mainwindow.h"  
#include "ui_mainwindow.h"  // 定义一个静态常量字符串,用于表示“不可用”或“无数据”的情况  
static const char blankString[] = QT_TRANSLATE_NOOP("SettingsDialog", "N/A");  // MainWindow类的构造函数实现  
MainWindow::MainWindow(QWidget *parent) :  QMainWindow(parent), // 调用基类的构造函数  ui(new Ui::MainWindow) { // 初始化UI类的指针  ui->setupUi(this); // 设置UI界面  // 初始化串口类的指针  serial = new QSerialPort;  // 用于存储串口描述、制造商、序列号等信息的字符串  QString description;  QString manufacturer;  QString serialNumber;  // 获取系统中可用的串口信息列表  QList<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts();  // 输出当前系统可以使用的串口个数  qDebug() << "Total numbers of ports: " << serialPortInfos.count();  // 遍历所有可用的串口信息,并将其添加到ComboBox中  for (const QSerialPortInfo &serialPortInfo : serialPortInfos) {  QStringList list; // 用于存储串口信息的字符串列表  description = serialPortInfo.description(); // 获取串口描述  manufacturer = serialPortInfo.manufacturer(); // 获取制造商信息  serialNumber = serialPortInfo.serialNumber(); // 获取序列号  // 将串口的相关信息添加到列表中,如果信息为空则使用blankString代替  list << serialPortInfo.portName()  << (!description.isEmpty() ? description : blankString)  << (!manufacturer.isEmpty() ? manufacturer : blankString)  << (!serialNumber.isEmpty() ? serialNumber : blankString)  << serialPortInfo.systemLocation()  << (serialPortInfo.vendorIdentifier() ? QString::number(serialPortInfo.vendorIdentifier(), 16) : blankString)  << (serialPortInfo.productIdentifier() ? QString::number(serialPortInfo.productIdentifier(), 16) : blankString);  // 将串口名称作为第一项,其余信息作为关联数据添加到ComboBox中  ui->comboBox_serialPort->addItem(list.first(), list);  }  // 添加一个自定义选项到串口ComboBox中  ui->comboBox_serialPort->addItem(tr("custom"));  // 设置波特率的ComboBox选项  ui->comboBox_baudRate->addItem(QStringLiteral("9600"), QSerialPort::Baud9600);  ui->comboBox_baudRate->addItem(QStringLiteral("19200"), QSerialPort::Baud19200);  ui->comboBox_baudRate->addItem(QStringLiteral("38400"), QSerialPort::Baud38400);  ui->comboBox_baudRate->addItem(QStringLiteral("115200"), QSerialPort::Baud115200);  ui->comboBox_baudRate->addItem(tr("Custom"));  // 设置数据位的ComboBox选项  ui->comboBox_dataBits->addItem(QStringLiteral("5"), QSerialPort::Data5);  ui->comboBox_dataBits->addItem(QStringLiteral("6"), QSerialPort::Data6);  ui->comboBox_dataBits->addItem(QStringLiteral("7"), QSerialPort::Data7);  ui->comboBox_dataBits->addItem(QStringLiteral("8"), QSerialPort::Data8);  ui->comboBox_dataBits->setCurrentIndex(3); // 默认选择8数据位  // 设置奇偶校验位的ComboBox选项  ui->comboBox_parity->addItem(tr("None"), QSerialPort::NoParity);  ui->comboBox_parity->addItem(tr("Even"), QSerialPort::EvenParity);  ui->comboBox_parity->addItem(tr("Odd"), QSerialPort::OddParity);  ui->comboBox_parity->addItem(tr("Mark"), QSerialPort::MarkParity);  ui->comboBox_parity->addItem(tr("Space"), QSerialPort::SpaceParity);  // 设置停止位的ComboBox选项  ui->comboBox_stopBit->addItem(QStringLiteral("1"), QSerialPort::OneStop);  ui->comboBox_stopBit->addItem(QStringLiteral("2"), QSerialPort::TwoStop);  // 设置流控的ComboBox选项  ui->comboBox_flowBit->addItem(tr("None"), QSerialPort::NoFlowControl);  ui->comboBox_flowBit->addItem(tr("RTS/CTS"), QSerialPort::HardwareControl);  ui->comboBox_flowBit->addItem(tr("XON/XOFF"), QSerialPort::SoftwareControl);  // 初始禁用发送按钮  ui->btn_send->setEnabled(false);  
}  // MainWindow类的析构函数实现  
MainWindow::~MainWindow() {  delete ui; // 释放UI类的指针所指向的内存  
}  // 打开串口按钮的槽函数实现  
void MainWindow::on_btn_openConsole_clicked() {  qDebug() << ui->btn_openConsole->text(); // 输出按钮的当前文本  if (ui->btn_openConsole->text() == tr("打开串口")) {  // 配置串口参数  serial->setPortName(ui->comboBox_serialPort->currentText()); // 设置串口名称  serial->setBaudRate(ui->comboBox_baudRate->currentText().toInt()); // 设置波特率  serial->setDataBits(QSerialPort::Data8); // 设置数据位  serial->setParity(QSerialPort::NoParity); // 设置奇偶校验位  serial->setStopBits(QSerialPort::OneStop); // 设置停止位  serial->setFlowControl(QSerialPort::NoFlowControl); // 设置流控  // 尝试打开串口  if (serial->open(QIODevice::ReadWrite)) {  // 禁用串口配置相关的ComboBox  ui->comboBox_baudRate->setEnabled(false);  ui->comboBox_dataBits->setEnabled(false);  ui->comboBox_flowBit->setEnabled(false);  ui->comboBox_parity->setEnabled(false);  ui->comboBox_serialPort->setEnabled(false);  ui->comboBox_stopBit->setEnabled(false);  // 启用发送按钮  ui->btn_send->setEnabled(true);  // 更改按钮文本  ui->btn_openConsole->setText(tr("关闭串口"));  // 连接串口的readyRead信号到readData槽函数  connect(serial, &QSerialPort::readyRead, this, &MainWindow::readData);  }  } else {  // 关闭串口  serial->close(); // 关闭串口连接  // 重新启用串口配置相关的ComboBox  ui->comboBox_baudRate->setEnabled(true);  ui->comboBox_dataBits->setEnabled(true);  ui->comboBox_flowBit->setEnabled(true);  ui->comboBox_parity->setEnabled(true);  ui->comboBox_serialPort->setEnabled(true);  ui->comboBox_stopBit->setEnabled(true);  // 更改按钮文本  ui->btn_openConsole->setText(tr("打开串口"));  // 禁用发送按钮  ui->btn_send->setEnabled(false);  }  
}  // 发送数据按钮的槽函数实现  
void MainWindow::on_btn_send_clicked() {  // 将发送文本框中的内容以Latin1编码写入串口  serial->write(ui->textEdit_send->toPlainText().toLatin1());  
}  // 清空接收数据按钮的槽函数实现  
void MainWindow::on_btn_clearRecv_clicked() {  ui->textEdit_recv->clear(); // 清空接收文本框的内容  
}  // 清空发送数据按钮的槽函数实现  
void MainWindow::on_btn_clearSend_clicked() {  ui->textEdit_send->clear(); // 清空发送文本框的内容  
}  // 读取串口数据的槽函数实现  
void MainWindow::readData() {  QByteArray buf; // 定义一个字节数组用于存储读取到的数据  qDebug() << "readData: " << endl; // 输出调试信息  buf = serial->readAll(); // 从串口读取所有可用数据  if (!buf.isEmpty()) { // 如果读取到的数据不为空  QString str = ui->textEdit_recv->toPlainText(); // 获取接收文本框的当前内容  str += tr(buf); // 将读取到的数据追加到字符串中(注意:这里tr的使用可能是不必要的,除非需要翻译字节数组的内容)  ui->textEdit_recv->clear(); // 清空接收文本框的内容  ui->textEdit_recv->append(str); // 将更新后的字符串添加到接收文本框中  }  
}

相关文章:

QT开发--串口通信

第十六章 串口通信 16.1 串口通信基础 串口通信主要通过DB9接口&#xff0c;适用于短距离&#xff08;<10米&#xff09;。关键参数包括&#xff1a; 波特率&#xff1a;每秒传输bit数&#xff0c;如9600。数据位&#xff1a;信息包中的有效数据位数。停止位&#xff1a;…...

数据库(至少还的再花两天 )

1 连接查询 左连接 右连接 2 聚合函数 SQL 统计求和 求最值 count sum avg max min 3 SQL关键字 limit 分页 group by 分组 distinct 去重 4 Select执行顺序 from where group by order by 5 数据库三范式 原子性 唯一性 直接性 6 存储引擎 MyISAM InnoDB 7 …...

网络安全公司及其主要产品介绍

以下是一些全球领先的网络安全公司及其主要产品介绍&#xff1a; 一、思科&#xff08;Cisco&#xff09; 思科是全球最大的网络设备供应商之一&#xff0c;其网络安全产品以企业级解决方案为主&#xff0c;覆盖多种安全需求。 Cisco ASA&#xff08;Adaptive Security Appli…...

orjson:高性能的Python JSON库

在Python中处理JSON数据是一项常见任务,标准库的json模块虽然功能齐全,但在性能方面还有提升空间。今天我要向大家介绍一个出色的第三方JSON库 - orjson。 orjson简介 orjson是一个快速、正确的Python JSON库。它具有以下主要特点: 性能卓越 - 在序列化和反序列化方面都比标准…...

常见几大排序算法

排序算法是计算机科学中的基本算法&#xff0c;它们将一个无序的数组或列表按特定顺序进行排列&#xff08;如升序或降序&#xff09;。常见的排序算法可以根据其时间复杂度、空间复杂度和适用场景分类。以下是几种常见的排序算法&#xff1a; 1. 冒泡排序&#xff08;Bubble …...

Linux下CMake入门

CMake的基础知识 什么是 CMake CMake 是一个跨平台的构建工具&#xff0c;主要用于管理构建过程。CMake 不直接构建项目&#xff0c;而是生成特定平台上的构建系统&#xff08;如 Unix 下的 Makefile&#xff0c;Windows 下的 Visual Studio 工程&#xff09;&#xff0c;然后…...

网络资源模板--Android Studio 实现简易记事本App

目录 一、项目演示 二、项目测试环境 三、项目详情 四、完整的项目源码 一、项目演示 网络资源模板--基于Android studio 实现的简易记事本App 二、项目测试环境 三、项目详情 首页 创建一个空的笔记本列表 mNotebookList。使用该列表和指定的布局资源 item_notebook 创建…...

根据Vue对比来深入学习React 下 props 组件传值 插槽 样式操作 hooks 高阶组件 性能优化

文章目录 函数组件的特点props组件间的传值父传子看上例子传父兄弟组件传值祖先组件传值 插槽基础插槽具名插槽作用域插槽 样式操作**CSS Modules** 生命周期useRef常用hookuseStateuseEffectuseContextuseReduceruseMemouseCallback 高阶组件什么时候使用 react性能问题和优化…...

HTML(六)超链接

HTML讲解&#xff08;一&#xff09;body部分_html body-CSDN博客 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>title</title> </head><body><a href"https://blog.csdn.net/2301_8034953…...

【Coroutines】Implement Lua Coroutine by Kotlin - 2

Last Chapter Link 文章目录 Symmetric CoroutinesNon-Symmetric Coroutine SampleSymmetric Coroutine SampleHow to Implement Symmetric CoroutinesWonderful TricksCode DesignTail Recursion OptimizationFull Sources Symmetric Coroutines in last blog, we have talk…...

java计算机毕设课设—扫雷游戏(附源码、文章、相关截图、部署视频)

这是什么系统&#xff1f; 资源获取方式再最下方&#xff08;本次10月份活动福利&#xff0c;免费提供下载&#xff0c;自行到对应的方式1下载&#xff0c;csdn的0积分下载&#xff09; java计算机毕设课设—扫雷游戏(附源码、文章、相关截图、部署视频) 基于Java的扫雷游戏…...

AndroidLogger 使用问题

Q1&#xff1a;解压zip后&#xff0c;启动Notepad未看到AndroidLogger工具栏 请检查plugins下安装位置是否正确&#xff0c;必须与下图一致&#xff0c;再确认Notepad 是否为 x64 &#xff1f; Q2&#xff1a;使用 adb 可以显示已连接&#xff0c;但是获取不到日志 暂时不确定问…...

数据库常见面试

8道面试题 目录 目录 7道面试题 1.怎样进行sql优化 4、group by优化 5、limit优化 6、count优化 7、update优化 2.。怎样查看sql执行情况呢(哪个关键字)&#xff0c;说说你对这个关键字的认识 4) possible_key&#xff1a; 5) key 3.说说你对innodb和 myisam的理解 …...

boxplot 绘制箱线图,添加数据点

先看效果图 import matplotlib.pyplot as plt #! 解决不显示的问题&#xff1a;中文设置为宋体格式 plt.rcParams[font.family] ["Times New Roman", SimSun]def plot_boxplot(data_list, out_file, x_custom_labels):# 画图fig, ax plt.subplots(figsize(90, 6…...

用sdkman管理多个jdk切换

前言 最近项目前后端进行升级&#xff0c;需要在jdk8和jdk17两个版本切换。最简单的是通过手动切换&#xff0c;但切换过程太繁琐&#xff0c;修改环境变量&#xff0c;达到切换目的。于是尝试其它解决方案&#xff0c;最终确实使用sdkman工具。 sdkman 是一款面向Java开发者的…...

【AIGC】ChatGPT提示词Prompt高效编写模式:结构化Prompt、提示词生成器与单样本/少样本提示

&#x1f4af;前言 在如今AI技术迅猛发展的背景下&#xff0c;尽管像ChatGPT这样的大型语言模型具备强大的生成能力&#xff0c;但它们的输出质量有时仍难以完全满足我们的预期。为了让ChatGPT生成更加准确、可靠的内容&#xff0c;掌握高效的Prompt编写技巧变得尤为重要。本文…...

反调式实战(有道翻译窗口弹出)

1.添加脚本断点实现源码获取 2.Function构造器构造debugger 因为是窗口被弹出的情况&#xff0c;所以window.closefunction()构造debugger。 3.定位到影响弹出的JavaScript代码片段 反调试思想&#xff1a;置空和替换&#xff0c;所以将其JavaScript进行注释或者删除。 这里主…...

verilog端口使用注意事项

下图存在组合逻辑反馈环&#xff0c;即组合逻辑的输出反馈到输入(赋值的左右2边存在相同的信号)&#xff0c;此种情况会造成系统不稳定。比如在data_in20的情况下&#xff0c;在data_out0 时候&#xff0c;输出的数据会反馈到输入&#xff0c;输入再输出&#xff0c;从而造成不…...

Docker常用命令大全汇总

Docker是一种流行的容器化平台,可以在一个独立的、隔离的环境中构建、部署和运行应用程序。了解Docker常用命令可以帮助我们更高效地管理容器,快速开发和部署应用。本文将整理一系列Docker的常用命令,便于日常使用和学习。 1 Docker基础命令 1.1 启动/停止/重启docker # …...

LVS-DR+Keepalived 高可用群集部署

LVS-DRKeepalived 高可用群集部署 Keepalived 的工作原理LVSKeepalived 高可用群集部署配置负载调度器&#xff08;主、备相同&#xff09;关闭防火墙和核心防护及准备IPVS模块配置keeplived&#xff08;主、备DR 服务器上都要设置&#xff09;启动 ipvsadm 服务调整 proc 响应…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...