GrabBag/AppUtils/UICommon/Src/DeviceStatusWidget.cpp

594 lines
20 KiB
C++
Raw Normal View History

#include "DeviceStatusWidget.h"
#include "ui_DeviceStatusWidget.h"
#include <QPainter>
#include <QBrush>
2025-12-12 00:31:21 +08:00
#include <QThread>
#include "VrLog.h"
DeviceStatusWidget::DeviceStatusWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::DeviceStatusWidget)
, m_selectedCameraIndex(0)
, m_camera1Name("")
, m_camera2Name("")
, m_robotVisible(true)
, m_cameraCount(1)
, m_cameraClickable(true)
{
ui->setupUi(this);
// 强制应用样式表 - 这是关键!
this->setAttribute(Qt::WA_StyledBackground, true);
// 初始化时设置样式
setItemStyle();
// 初始化点击事件
initClickEvents();
// 初始化设备状态(离线状态)
updateCamera1Status(false);
updateCamera2Status(false);
updateRobotStatus(false);
setCameraSelectedStyle(1);
}
DeviceStatusWidget::~DeviceStatusWidget()
{
delete ui;
}
// 设置devstatus的样式
void DeviceStatusWidget::setItemStyle()
{
this->setStyleSheet(
"DeviceStatusWidget { "
" border: 2px solid #191A1C; " // 添加边框作为格子间分隔线
" border-radius: 0px; " // 去掉圆角,让分隔线更清晰
"} "
);
}
// 设置相机状态图片的私有成员函数
void DeviceStatusWidget::setCameraStatusImage(QWidget* widget, bool isOnline) {
if (!widget) return;
QString imagePath = isOnline ? ":/common/resource/camera_online.png" : ":/common/resource/camera_offline.png";
widget->setStyleSheet(QString("image: url(%1);").arg(imagePath));
// widget->setFixedSize(32, 32); // 调整大小以适应图片
}
// 设置机械臂状态图片的私有成员函数
void DeviceStatusWidget::setRobotStatusImage(QWidget* widget, bool isOnline) {
if (!widget) return;
QString imagePath = isOnline ? ":/common/resource/robot_online.png" : ":/common/resource/robot_offline.png";
widget->setStyleSheet(QString("image: url(%1); ").arg(imagePath));
// widget->setFixedSize(32, 32); // 调整大小以适应图片
}
// 设置相机1名称
void DeviceStatusWidget::setCamera1Name(const QString& name)
{
2025-12-12 00:31:21 +08:00
// 检查是否在主线程中
if (QThread::currentThread() == this->thread()) {
// 在主线程中,直接执行
m_camera1Name = name;
// 如果相机1已经初始化过状态则更新显示文本
if (ui->dev_camera_1_txt) {
updateCamera1DisplayText(ui->dev_camera_1_txt->text().contains("在线"));
}
} else {
// 在其他线程中,使用队列方式确保线程安全
QMetaObject::invokeMethod(this, [this, name]() {
m_camera1Name = name;
// 如果相机1已经初始化过状态则更新显示文本
if (ui->dev_camera_1_txt) {
updateCamera1DisplayText(ui->dev_camera_1_txt->text().contains("在线"));
}
}, Qt::QueuedConnection);
}
}
// 设置相机2名称
void DeviceStatusWidget::setCamera2Name(const QString& name)
{
2025-12-12 00:31:21 +08:00
// 检查是否在主线程中
if (QThread::currentThread() == this->thread()) {
// 在主线程中,直接执行
m_camera2Name = name;
// 如果相机2已经初始化过状态则更新显示文本
if (ui->dev_camera_2_txt) {
updateCamera2DisplayText(ui->dev_camera_2_txt->text().contains("在线"));
}
} else {
// 在其他线程中,使用队列方式确保线程安全
QMetaObject::invokeMethod(this, [this, name]() {
m_camera2Name = name;
// 如果相机2已经初始化过状态则更新显示文本
if (ui->dev_camera_2_txt) {
updateCamera2DisplayText(ui->dev_camera_2_txt->text().contains("在线"));
}
}, Qt::QueuedConnection);
}
}
// 更新相机1状态
void DeviceStatusWidget::updateCamera1Status(bool isConnected)
{
2025-12-12 00:31:21 +08:00
// 检查是否在主线程中
if (QThread::currentThread() == this->thread()) {
// 在主线程中,直接执行
setCameraStatusImage(ui->dev_camer_1_img, isConnected);
updateCamera1DisplayText(isConnected);
} else {
// 在其他线程中,使用队列方式确保线程安全
QMetaObject::invokeMethod(this, [this, isConnected]() {
setCameraStatusImage(ui->dev_camer_1_img, isConnected);
updateCamera1DisplayText(isConnected);
}, Qt::QueuedConnection);
}
}
// 更新相机2状态
void DeviceStatusWidget::updateCamera2Status(bool isConnected)
{
2025-12-12 00:31:21 +08:00
// 检查是否在主线程中
if (QThread::currentThread() == this->thread()) {
// 在主线程中,直接执行
setCameraStatusImage(ui->dev_camer_2_img, isConnected);
updateCamera2DisplayText(isConnected);
} else {
// 在其他线程中,使用队列方式确保线程安全
QMetaObject::invokeMethod(this, [this, isConnected]() {
setCameraStatusImage(ui->dev_camer_2_img, isConnected);
updateCamera2DisplayText(isConnected);
}, Qt::QueuedConnection);
}
}
// 更新相机1显示文本的私有方法
void DeviceStatusWidget::updateCamera1DisplayText(bool isConnected)
{
QString statusText;
if (m_camera1Name.isEmpty()) {
statusText = isConnected ? "相机1在线" : "相机1离线";
} else {
statusText = isConnected ? QString("%1在线").arg(m_camera1Name) : QString("%1离线").arg(m_camera1Name);
}
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
ui->dev_camera_1_txt->setText(statusText);
ui->dev_camera_1_txt->setStyleSheet(colorStyle);
}
// 更新相机2显示文本的私有方法
void DeviceStatusWidget::updateCamera2DisplayText(bool isConnected)
{
QString statusText;
if (m_camera2Name.isEmpty()) {
statusText = isConnected ? "相机2在线" : "相机2离线";
} else {
statusText = isConnected ? QString("%1在线").arg(m_camera2Name) : QString("%1离线").arg(m_camera2Name);
}
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
ui->dev_camera_2_txt->setText(statusText);
ui->dev_camera_2_txt->setStyleSheet(colorStyle);
}
// 更新机械臂状态
void DeviceStatusWidget::updateRobotStatus(bool isConnected)
{
2025-12-12 00:31:21 +08:00
// 检查是否在主线程中
if (QThread::currentThread() == this->thread()) {
// 在主线程中,直接执行
setRobotStatusImage(ui->dev_robot_img, isConnected);
2025-12-12 00:31:21 +08:00
QString statusText = isConnected ? "机械臂在线" : "机械臂离线";
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
ui->dev_robot_txt->setText(statusText);
ui->dev_robot_txt->setStyleSheet(colorStyle);
} else {
// 在其他线程中,使用队列方式确保线程安全
QMetaObject::invokeMethod(this, [this, isConnected]() {
setRobotStatusImage(ui->dev_robot_img, isConnected);
QString statusText = isConnected ? "机械臂在线" : "机械臂离线";
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
2025-12-12 00:31:21 +08:00
ui->dev_robot_txt->setText(statusText);
ui->dev_robot_txt->setStyleSheet(colorStyle);
}, Qt::QueuedConnection);
}
}
// 更新串口状态
void DeviceStatusWidget::updateSerialStatus(bool isConnected)
{
2025-12-12 00:31:21 +08:00
// 检查是否在主线程中
if (QThread::currentThread() == this->thread()) {
// 在主线程中,直接执行
setRobotStatusImage(ui->dev_robot_img, isConnected);
QString statusText = isConnected ? "RS485在线" : "RS485离线";
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
ui->dev_robot_txt->setText(statusText);
ui->dev_robot_txt->setStyleSheet(colorStyle);
} else {
// 在其他线程中,使用队列方式确保线程安全
QMetaObject::invokeMethod(this, [this, isConnected]() {
setRobotStatusImage(ui->dev_robot_img, isConnected);
QString statusText = isConnected ? "RS485在线" : "RS485离线";
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
ui->dev_robot_txt->setText(statusText);
ui->dev_robot_txt->setStyleSheet(colorStyle);
}, Qt::QueuedConnection);
}
}
// 设置相机数量(用于控制相机二的显示和布局方向)
void DeviceStatusWidget::setCameraCount(int cameraCount)
{
2025-12-12 00:31:21 +08:00
LOG_DEBUG("setCameraCount cameraCount: %d \n", cameraCount);
// 检查是否在主线程中
if (QThread::currentThread() == this->thread()) {
m_cameraCount = cameraCount;
2025-12-12 00:31:21 +08:00
// 如果相机数量小于2隐藏相机二相关的UI元素
bool showCamera2 = (cameraCount >= 2);
LOG_DEBUG("showCamera2: %d \n", showCamera2);
// 隐藏整个相机2的frame而不是单独隐藏图片和文字
if (ui->frame_camera_2) {
ui->frame_camera_2->setVisible(showCamera2);
} else {
// 如果frame_camera_2不存在则单独隐藏图片和文字控件
if (ui->dev_camer_2_img) {
ui->dev_camer_2_img->setVisible(showCamera2);
}
if (ui->dev_camera_2_txt) {
ui->dev_camera_2_txt->setVisible(showCamera2);
}
}
// 更新布局
updateLayout();
} else {
2025-12-12 00:31:21 +08:00
// 在其他线程中,使用队列方式确保线程安全
QMetaObject::invokeMethod(this, [this, cameraCount]() {
m_cameraCount = cameraCount;
2025-12-12 00:31:21 +08:00
// 如果相机数量小于2隐藏相机二相关的UI元素
bool showCamera2 = (cameraCount >= 2);
LOG_DEBUG("showCamera2: %d \n", showCamera2);
// 隐藏整个相机2的frame而不是单独隐藏图片和文字
if (ui->frame_camera_2) {
ui->frame_camera_2->setVisible(showCamera2);
} else {
// 如果frame_camera_2不存在则单独隐藏图片和文字控件
if (ui->dev_camer_2_img) {
ui->dev_camer_2_img->setVisible(showCamera2);
}
if (ui->dev_camera_2_txt) {
ui->dev_camera_2_txt->setVisible(showCamera2);
}
}
// 更新布局
updateLayout();
}, Qt::QueuedConnection);
}
}
// 设置机械臂是否可见
void DeviceStatusWidget::setRobotVisible(bool visible)
{
LOG_DEBUG("setRobotVisible visible: %d \n", visible);
// 检查是否在主线程中
if (QThread::currentThread() == this->thread()) {
m_robotVisible = visible;
// 隐藏机械臂的frame
if (ui->frame_robot) {
ui->frame_robot->setVisible(visible);
}
// 更新布局
updateLayout();
} else {
// 在其他线程中,使用队列方式确保线程安全
QMetaObject::invokeMethod(this, [this, visible]() {
m_robotVisible = visible;
// 隐藏机械臂的frame
if (ui->frame_robot) {
ui->frame_robot->setVisible(visible);
2025-12-12 00:31:21 +08:00
}
// 更新布局
updateLayout();
2025-12-12 00:31:21 +08:00
}, Qt::QueuedConnection);
}
}
// 设置相机是否可点击
void DeviceStatusWidget::setCameraClickable(bool clickable)
{
LOG_DEBUG("setCameraClickable clickable: %d \n", clickable);
m_cameraClickable = clickable;
// 更新鼠标样式
Qt::CursorShape cursor = clickable ? Qt::PointingHandCursor : Qt::ArrowCursor;
if (ui->frame_camera_1) {
ui->frame_camera_1->setCursor(cursor);
}
if (ui->frame_camera_2) {
ui->frame_camera_2->setCursor(cursor);
}
// 如果禁用点击,清除选中样式
if (!clickable) {
if (ui->frame_camera_1) {
ui->frame_camera_1->setStyleSheet("background-color: rgb(37, 38, 42);");
}
if (ui->frame_camera_2) {
ui->frame_camera_2->setStyleSheet("background-color: rgb(37, 38, 42);");
}
}
}
// 更新布局(根据相机数量和机械臂可见性)
void DeviceStatusWidget::updateLayout()
{
LOG_DEBUG("updateLayout: cameraCount=%d, robotVisible=%d\n", m_cameraCount, m_robotVisible);
if (m_cameraCount >= 2) {
// 多相机:纵向排列
setVerticalLayout();
} else if (m_robotVisible) {
// 单相机 + 机械臂可见:横向排列,相机和机械臂各占一半
setHorizontalLayoutForSingleCamera();
} else {
// 单相机 + 机械臂隐藏:相机占满整个容器
this->setMinimumSize(180, 80);
this->resize(180, 80);
int frameWidth = 556; // 整个容器宽度
int frameHeight = 70;
// 相机1占满整个容器
if (ui->frame_camera_1) {
ui->frame_camera_1->setGeometry(0, 0, frameWidth, frameHeight);
centerFrameContents(ui->frame_camera_1, frameHeight);
}
LOG_DEBUG("Layout adjusted for single camera without robot\n");
}
}
// 设置为纵向布局
void DeviceStatusWidget::setVerticalLayout()
{
// 调整主widget的尺寸以适应纵向布局
this->setMinimumSize(180, 198);
this->resize(180, 198);
// 通过调整每个frame的尺寸和位置来模拟垂直布局
adjustFramesForVerticalLayout();
LOG_DEBUG("Layout adjusted for vertical display\n");
}
// 设置为单相机横向布局(相机和机械臂各占一半)
void DeviceStatusWidget::setHorizontalLayoutForSingleCamera()
{
// 设置单相机模式的widget尺寸
this->setMinimumSize(180, 80);
this->resize(180, 80);
// 调整为单相机横向布局的frame尺寸和位置
adjustFramesForSingleCameraHorizontalLayout();
LOG_DEBUG("Layout adjusted for single camera horizontal display\n");
}
// 调整frames为纵向排列
void DeviceStatusWidget::adjustFramesForVerticalLayout()
{
int frameWidth = 180;
int spacing = 2;
int startY = 0;
// 计算可见项数量
int visibleCount = 0;
bool showCamera2 = (m_cameraCount >= 2);
if (ui->frame_camera_1) visibleCount++;
if (showCamera2 && ui->frame_camera_2) visibleCount++;
if (m_robotVisible && ui->frame_robot) visibleCount++;
// 根据可见项数量计算每个frame的高度
int totalHeight = 198; // 总高度
int frameHeight = (visibleCount > 0) ? (totalHeight - (visibleCount - 1) * spacing) / visibleCount : 64;
// 重新定位frame的位置以实现纵向排列效果
if (ui->frame_camera_1) {
ui->frame_camera_1->setGeometry(0, startY, frameWidth, frameHeight);
centerFrameContents(ui->frame_camera_1, frameHeight);
startY += frameHeight + spacing;
}
if (showCamera2 && ui->frame_camera_2) {
ui->frame_camera_2->setGeometry(0, startY, frameWidth, frameHeight);
centerFrameContents(ui->frame_camera_2, frameHeight);
startY += frameHeight + spacing;
}
if (m_robotVisible && ui->frame_robot) {
ui->frame_robot->setGeometry(0, startY, frameWidth, frameHeight);
centerFrameContents(ui->frame_robot, frameHeight);
}
}
// 调整frames为单相机横向排列相机和机械臂各占一半
void DeviceStatusWidget::adjustFramesForSingleCameraHorizontalLayout()
{
int frameWidth = 275; // 每个frame占一半宽度 (180/2 - 5间距)
int frameHeight = 70;
int spacing = 6;
// 相机1占左半边
if (ui->frame_camera_1) {
ui->frame_camera_1->setGeometry(0, 0, frameWidth, frameHeight);
centerFrameContents(ui->frame_camera_1, frameHeight);
}
// 相机2隐藏在setCameraCount中已处理
// 机械臂占右半边
if (ui->frame_robot) {
ui->frame_robot->setGeometry(frameWidth + spacing, 0, frameWidth, frameHeight);
centerFrameContents(ui->frame_robot, frameHeight);
}
}
// 初始化点击事件
void DeviceStatusWidget::initClickEvents()
{
// 为相机1的frame添加点击事件
if (ui->frame_camera_1) {
ui->frame_camera_1->installEventFilter(this);
ui->frame_camera_1->setMouseTracking(true);
ui->frame_camera_1->setCursor(Qt::PointingHandCursor); // 设置鼠标样式为手型
}
// 为相机2的frame添加点击事件
if (ui->frame_camera_2) {
ui->frame_camera_2->installEventFilter(this);
ui->frame_camera_2->setMouseTracking(true);
ui->frame_camera_2->setCursor(Qt::PointingHandCursor); // 设置鼠标样式为手型
}
}
// 事件过滤器,处理鼠标点击事件
bool DeviceStatusWidget::eventFilter(QObject *obj, QEvent *event)
{
// 如果相机不可点击,不处理点击事件
if (!m_cameraClickable) {
return QWidget::eventFilter(obj, event);
}
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::LeftButton) {
if (obj == ui->frame_camera_1) {
onCamera1Clicked();
return true;
} else if (obj == ui->frame_camera_2) {
onCamera2Clicked();
return true;
}
}
}
return QWidget::eventFilter(obj, event);
}
// 相机1点击槽函数
void DeviceStatusWidget::onCamera1Clicked()
{
if (m_selectedCameraIndex != 1) {
m_selectedCameraIndex = 1;
setCameraSelectedStyle(1);
emit cameraClicked(1);
}
}
// 相机2点击槽函数
void DeviceStatusWidget::onCamera2Clicked()
{
if (m_selectedCameraIndex != 2) {
m_selectedCameraIndex = 2;
setCameraSelectedStyle(2);
emit cameraClicked(2);
}
}
// 设置相机选中状态的样式
void DeviceStatusWidget::setCameraSelectedStyle(int cameraIndex)
{
// 重置所有相机的样式
if (ui->frame_camera_1) {
ui->frame_camera_1->setStyleSheet("background-color: rgb(37, 38, 42);");
}
if (ui->frame_camera_2) {
ui->frame_camera_2->setStyleSheet("background-color: rgb(37, 38, 42);");
}
// 设置选中相机的样式
QString selectedStyle = "QFrame { background-color: rgb(67, 68, 72);}";
if (cameraIndex == 1 && ui->frame_camera_1) {
ui->frame_camera_1->setStyleSheet(selectedStyle);
} else if (cameraIndex == 2 && ui->frame_camera_2) {
ui->frame_camera_2->setStyleSheet(selectedStyle);
}
}
// 调整frame内部控件垂直居中
void DeviceStatusWidget::centerFrameContents(QFrame* frame, int frameHeight)
{
if (!frame) return;
// 图标尺寸
const int imgWidth = 48;
const int imgHeight = 48;
const int imgX = 20;
// 文字尺寸
const int txtWidth = 90;
const int txtHeight = 26;
const int txtX = 80;
// 计算垂直居中位置
int imgY = (frameHeight - imgHeight) / 2;
int txtY = (frameHeight - txtHeight) / 2;
// 根据frame名称找到对应的子控件并调整位置
if (frame == ui->frame_camera_1) {
if (ui->dev_camer_1_img) {
ui->dev_camer_1_img->setGeometry(imgX, imgY, imgWidth, imgHeight);
}
if (ui->dev_camera_1_txt) {
ui->dev_camera_1_txt->setGeometry(txtX, txtY, txtWidth, txtHeight);
}
} else if (frame == ui->frame_camera_2) {
if (ui->dev_camer_2_img) {
ui->dev_camer_2_img->setGeometry(imgX, imgY, imgWidth, imgHeight);
}
if (ui->dev_camera_2_txt) {
ui->dev_camera_2_txt->setGeometry(txtX, txtY, txtWidth, txtHeight);
}
} else if (frame == ui->frame_robot) {
if (ui->dev_robot_img) {
ui->dev_robot_img->setGeometry(imgX, imgY, imgWidth, imgHeight);
}
if (ui->dev_robot_txt) {
ui->dev_robot_txt->setGeometry(txtX, txtY, txtWidth, txtHeight);
}
}
}