Qwidget上显示视频,在视频上层画矩形框并且不闪烁

文章讲述了作者在实时视频上使用Qt进行矩形框标注时遇到的问题,通过创建透明widget和精细调整绘图策略解决了不透明背景和影响未画区域的问题,提供了一段包含鼠标移动和绘图事件处理的代码片段。

在工作中需要在实时视频上画矩形框,类似于深度学习里面的打标签。


根据别人的教程采用了Qt的事件过滤器采监控widget上鼠标事件和绘图事件但是画的矩形框并不显示。(实际上是因为本文后面阐述的那个问题,这种操作在Qlable显示的图像上画图是没问题的)


然后更改了策略,在widget上新建了一个透明widget
transparentWidget = new QWidget(ui->widget);
并设置其背景透明、设置其坐标与widget坐标相同,在transparentWidget上画矩形框。

但是又遇到了一个问题,画的矩形框中间部分不透明,一直显示背景颜色。将transparentWidget 放在其他界面上(如QLable显示的图片上)却没问题。猜测因该是因为将widget句柄交给海康的sdk用来显示实时视频的问题。于是更改了画矩形框的策略,我画了四条直线,这样就不会影响没画图的地方了。同事update()触发绘图事件的时候指定直线所在区域绘图。效果如下:

部分代码如下:
鼠标移动事件:
 

        if(event->type() == QEvent::MouseMove)
        {
            transparentWidget->setGeometry(ui->widget->geometry());
            QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
            if (_dragging)
            {
               qDebug() << "in trans mouse move";
               _endPoint = mouseEvent->pos();
               float startx=_startPoint.x();
               float starty=_startPoint.y();
               float endx=_endPoint.x();
               float endy=_endPoint.y();
               float weight=qAbs(startx-endx)+6;
               float height=qAbs(starty-endy)+6;
               QRect r1(QPoint(startx-3, starty-3), QSize(weight, 6));
               QRect r2(QPoint(startx-3, starty-3), QSize(6, height));
               QRect r3(QPoint(startx-3, endy-3), QSize(weight, 6));
               QRect r4(QPoint(endx-3, starty-3), QSize(6, height));
               transparentWidget->repaint(r1);
               transparentWidget->repaint(r2);
               transparentWidget->repaint(r3);
               transparentWidget->repaint(r4);

            }
            return true;
        }
绘图事件处理:
 

void MainWindow::paintRec()
{
    transparentWidget->setGeometry(ui->widget->geometry());
    qDebug() << "transparentWidget:" << transparentWidget->size() << transparentWidget->geometry();
    QPainter painterRec(transparentWidget);
    painterRec.setCompositionMode(QPainter::CompositionMode_SourceOver);

    painterRec.setPen(QPen(Qt::red, 5, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin));
    float startx=_startPoint.x();
    float starty=_startPoint.y();
    float endx=_endPoint.x();
    float endy=_endPoint.y();

    painterRec.drawLine(startx,starty,endx,starty);
    painterRec.drawLine(startx,starty,startx,endy);
    painterRec.drawLine(endx,starty,endx,endy);
    painterRec.drawLine(startx,endy,endx,endy);
}

算是采用了歪门邪道完成了任务,如果大家有更好的方法或者理解欢迎分享出来!

#include "o3_JTR.h" #include "DatabaseManager.h" TemperatureGauge::TemperatureGauge(QWidget *parent) :QWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(2,2,2,2); layout->setSpacing(2); gauge = new QProgressBar(); tempLabel = new QLabel(this); gauge->setRange(0,300); gauge->setValue(0); gauge->setOrientation(Qt::Vertical); gauge->setTextVisible(false); gauge->setStyleSheet("QProgressBar{border:2px solid #888;border-radius:5px;background:white;}" "QProgressBar::chunk{background:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #4CAF50, stop:0.5 #FFC107, stop:1 #F44336);border-radius:3px;}"); tempLabel->setAlignment(Qt::AlignCenter); tempLabel->setStyleSheet("font-size:12px;"); layout->addWidget(gauge,1); layout->addWidget(tempLabel); } void TemperatureGauge::setTemperatureDirect(float temp) { gauge->setValue(temp); tempLabel->setText(QString("%1°C").arg(temp)); gauge->setStyleSheet("QProgressBar::chunk{background:#c9ffa7;border-radius:3px;}"); } void TemperatureGauge::setTemperature(float temp) { gauge->setValue(temp); tempLabel->setText(QString("%1°C").arg(temp)); gauge->setStyleSheet("QProgressBar::chunk{background:#c9ffa7;border-radius:3px;}"); } JTR::JTR(QWidget *parent) :QWidget(parent),dbManager(DatabaseManager::instance()) { setUpUI(); // 立即创建条形图 createBarChart(); //定时刷新数据 refreshTimer = new QTimer(this); connect(refreshTimer, &QTimer::timeout, this, &JTR::refreshData); refreshTimer->start(2000); //每2秒刷新一次 // //延迟2秒后开启动 // QTimer::singleShot(2000, this, &JTR::startAnimation); } // void JTR::startAnimation() // { // if(animation) // { // movingRect->move(0, 10); //重置位置 // animation->start(); //开启动 // } // } //显示温度,进去板子数量,出来板子数量 void JTR::setUpUI() { QHBoxLayout* mainLayout = new QHBoxLayout(this); // 改为水平布局 mainLayout->setContentsMargins(10, 10, 10, 10); mainLayout->setSpacing(15); //左侧区域 - 温度显示 QWidget* tempWidget = new QWidget(); createTemperatureDisplay(tempWidget); //修改为在tempWidget上创建温度显示 //右侧区域 - 日志显示 QWidget* logWidget = new QWidget(); createLogDisplay(logWidget); //设置左右比例约为3:2 mainLayout->addWidget(tempWidget, 3); mainLayout->addWidget(logWidget, 2); this->setLayout(mainLayout); //初始化出板数定时器 (5s刷新一次) boardCountTimer = new QTimer(this); connect(boardCountTimer, &QTimer::timeout, this, &JTR::refreshBoardCount); boardCountTimer->start(5000); //5s QTimer::singleShot(0, this, &JTR::refreshBoardCount); //立即刷新一次 } void JTR::createLogDisplay(QWidget* parent) { QVBoxLayout* layout = new QVBoxLayout(parent); layout->setContentsMargins(5, 5, 5, 5); layout->setSpacing(10); QLabel* titleLabel = new QLabel("操作日志"); titleLabel->setStyleSheet("font-weight: bold; font-size: 14px;"); layout->addWidget(titleLabel); logDisplay = new QTextEdit(); logDisplay->setReadOnly(true); logDisplay->setStyleSheet( "font-family: 'Courier New';" "font-size: 12px;" "background-color: white;" "border: 1px solid #ccc;" ); layout->addWidget(logDisplay,1); //拉伸因子为1 //出板数显示区域 QHBoxLayout* boardCountLayout = new QHBoxLayout(); boardCountLabel = new QLabel("出板数:"); boardCountLabel->setStyleSheet("font-weight: bold; font-size: 14px;"); boardCountDisplay = new QTextEdit(); boardCountDisplay->setReadOnly(true); boardCountDisplay->setFixedHeight(30); boardCountDisplay->setStyleSheet( "font-size: 14px;" "background-color: white;" "border: 1px solid #ccc;" ); boardCountLayout->addWidget(boardCountLabel); boardCountLayout->addWidget(boardCountDisplay, 1); //设置拉伸因子 layout->addLayout(boardCountLayout); } //获取当天日期字符串 QString JTR::getTodayDateString() { return QDateTime::currentDateTime().toString("yyyy-MM-dd"); } void JTR::createBarChart() { QSqlDatabase db = dbManager.getDatabase(); QString today = getTodayDateString(); //获取今天的第一条数据的年月日 QString productionDateStr = "生产日期:" + today; QDateTime startTime = QDateTime::fromString(today + " 07:00:00", "yyyy-MM-dd hh:mm:ss"); //固定为当天7:00 QDateTime endTime = QDateTime::fromString(today + " 23:00:00", "yyyy-MM-dd hh:mm:ss"); //固定为当天23:00 //查询保持变 QString queryStr = QString( "SELECT " "CONCAT(LPAD(HOUR(record_time), 2, '0'), ':', " "IF(MINUTE(record_time) < 30, '00', '30')) AS time_slot, " "MAX(board_out_count) - MIN(board_out_count) + 1 AS board_count " "FROM board_count " "WHERE DATE(record_time) = '%1' " "AND TIME(record_time) BETWEEN '07:00:00' AND '23:00:00' " "GROUP BY HOUR(record_time), FLOOR(MINUTE(record_time)/30) " "ORDER BY time_slot" ).arg(today); QSqlQuery query(db); if(!query.exec(queryStr)) { qDebug() << "条形图查询失败:" << query.lastError().text(); // 即使查询失败也创建空图表,保证UI显示 createEmptyBarChart(productionDateStr); return; } //创建时间槽列表(7:00-23:00,半小时一个槽) QStringList timeSlots; QMap<QString, int> timeSlotCounts; //生成所有可能的时间槽并初始化为0 for(int hour = 7; hour <= 23; hour++) { timeSlots << QString("%1:00").arg(hour, 2, 10, QChar('0')); timeSlotCounts[QString("%1:00").arg(hour, 2, 10, QChar('0'))] = 0; if(hour < 23) { //23点添加30分(避免超过23:00) timeSlots << QString("%1:30").arg(hour, 2, 10, QChar('0')); timeSlotCounts[QString("%1:30").arg(hour, 2, 10, QChar('0'))] = 0; } } //填充查询结果 bool hasData = false; while(query.next()) { QString timeSlot = query.value("time_slot").toString(); int count = query.value("board_count").toInt(); timeSlotCounts[timeSlot] = count; if(count > 0) hasData = true; } //如果没有数据,创建空图表 if(!hasData) { createEmptyBarChart(productionDateStr); return; } //创建条形图 QChart* barChart = new QChart(); barChart->setAnimationOptions(QChart::SeriesAnimations); barChart->legend()->hide(); //设置标题样式 QFont titleFont = barChart->titleFont(); titleFont.setPointSize(10); titleFont.setBold(true); barChart->setTitleFont(titleFont); //设置标题内容 barChart->setTitle(QString("生产数量统计(半小时) - %1").arg(productionDateStr)); QBarSeries *series = new QBarSeries(); QBarSet *set = new QBarSet("生产数量"); set->setBrush(QBrush(QColor("#4CAF50"))); int maxCount = 0; foreach(const QString &slot, timeSlots) { int count = timeSlotCounts[slot]; *set << count; if(count > maxCount) { maxCount = count; } } maxCount = ((maxCount / 10) + 1) * 10; if(maxCount < 15) maxCount = 15; series->append(set); barChart->addSeries(series); //===== 使用 QCategoryAxis ===== QCategoryAxis *axisX = new QCategoryAxis(); axisX->setMin(0); axisX->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue); //创建时间标签(7:00-23:00,每半小时一个刻度) QStringList timeLabels; QList<double> tickPositions; for(int hour = 7; hour <= 23; hour++) { //整点标签和位置 timeLabels << QString("%1:00").arg(hour, 2, 10, QChar('0')); tickPositions << (hour - 7) * 2; //转换为0-based索引(每半小时=1单位) //半点标签和位置(23:00除外) if(hour < 23) { timeLabels << QString("%1:30").arg(hour, 2, 10, QChar('0')); tickPositions << (hour - 7) * 2 + 1; } } //设置分类边界(关键修正:直接使用刻度位置) for(int i = 0; i < timeLabels.size(); i++) { axisX->append(timeLabels[i], tickPositions[i] - 0.5); } //设置轴范围(覆盖所有时间点) axisX->setRange(0, tickPositions.last()); //其他设置 axisX->setTitleText("时间"); axisX->setLabelsAngle(-90); //设置Y轴(数量) QValueAxis *axisY = new QValueAxis(); axisY->setRange(0, maxCount); axisY->setTitleText("数量"); axisY->setTickCount((maxCount / 5) + 1); axisY->setLabelFormat("%d"); //将轴添加到图表并将系列附加到轴 barChart->addAxis(axisX, Qt::AlignBottom); barChart->addAxis(axisY, Qt::AlignLeft); series->attachAxis(axisX); series->attachAxis(axisY); //在条的顶部添加值标签 series->setLabelsVisible(true); series->setLabelsFormat("@value"); series->setLabelsPosition(QAbstractBarSeries::LabelsCenter); //调整图表边距,确保标签显示完整 barChart->setMargins(QMargins(30, 10, 30, 30)); //设置条形宽度 series->setBarWidth(0.9); //适当调整条形宽度 //将图表应用于视图 if(barChartView->chart()) { delete barChartView->chart(); } barChartView->setChart(barChart); barChartView->setRenderHint(QPainter::Antialiasing); } // 创建空条形图 void JTR::createEmptyBarChart(const QString& title) { QChart* barChart = new QChart(); barChart->setAnimationOptions(QChart::SeriesAnimations); barChart->legend()->hide(); QFont titleFont = barChart->titleFont(); titleFont.setPointSize(10); titleFont.setBold(true); barChart->setTitleFont(titleFont); barChart->setTitle(QString("生产数量统计(半小时) - %1").arg(title)); QBarSeries *series = new QBarSeries(); QBarSet *set = new QBarSet("生产数量"); set->setBrush(QBrush(QColor("#4CAF50"))); // 添加空数据 for(int i = 0; i < 32; i++) // 7:00-23:00共32个半小时段 { *set << 0; } series->append(set); barChart->addSeries(series); // 创建X轴 QCategoryAxis *axisX = new QCategoryAxis(); axisX->setMin(0); axisX->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue); QStringList timeLabels; QList<double> tickPositions; for(int hour = 7; hour <= 23; hour++) { timeLabels << QString("%1:00").arg(hour, 2, 10, QChar('0')); tickPositions << (hour - 7) * 2; if(hour < 23) { timeLabels << QString("%1:30").arg(hour, 2, 10, QChar('0')); tickPositions << (hour - 7) * 2 + 1; } } for(int i = 0; i < timeLabels.size(); i++) { axisX->append(timeLabels[i], tickPositions[i] - 0.5); } axisX->setRange(0, tickPositions.last()); axisX->setTitleText("时间"); axisX->setLabelsAngle(-90); // 创建Y轴 QValueAxis *axisY = new QValueAxis(); axisY->setRange(0, 10); // 默认范围0-10 axisY->setTitleText("数量"); axisY->setTickCount(3); axisY->setLabelFormat("%d"); barChart->addAxis(axisX, Qt::AlignBottom); barChart->addAxis(axisY, Qt::AlignLeft); series->attachAxis(axisX); series->attachAxis(axisY); barChart->setMargins(QMargins(30, 10, 30, 30)); if(barChartView->chart()) { delete barChartView->chart(); } barChartView->setChart(barChart); barChartView->setRenderHint(QPainter::Antialiasing); } void JTR::updateBarChart() { createBarChart(); // 直接调用现有的创建函数 } //创建温区 void JTR::createTemperatureDisplay(QWidget* parent) { QVBoxLayout* vLayout = new QVBoxLayout(parent); vLayout->setSpacing(15); //创建上层温区 QHBoxLayout* upperHeaderLayout = new QHBoxLayout(); upperHeaderLayout->addSpacing(0); //增加间距对齐 for(int i = 1;i < 11;++i) { QLabel* numLabel = new QLabel(QString::number(i)); numLabel->setAlignment(Qt::AlignCenter); numLabel->setFixedWidth(50); upperHeaderLayout->addWidget(numLabel); } vLayout->addLayout(upperHeaderLayout,1); //创建上层温区 QGroupBox* upGroup = new QGroupBox("上层温区"); QHBoxLayout* upLayout = new QHBoxLayout(); upLayout->setSpacing(15); for(int i =0;i < 10;++i) { TemperatureGauge* gauge = new TemperatureGauge(); gauge->setFixedSize(60,150); upLayout->addWidget(gauge); upGauges.append(gauge); } upGroup->setLayout(upLayout); vLayout->addWidget(upGroup,2); // //创建动区域 // animationWidget = new QWidget(); // animationWidget->setFixedHeight(30); // animationWidget->setStyleSheet("background-color:white;"); // animLayout = new QHBoxLayout(animationWidget); // animLayout->setContentsMargins(0,0,0,0); // animLayout->setSpacing(0); // //创建十个动片段 // for(int i = 0;i < 10;++i) // { // QWidget* segment = new QWidget(); // segment->setStyleSheet("background-color:transparent;border-right:1px dashed #888"); // segment->setFixedWidth(60); // animLayout->addWidget(segment); // } // //创建移动的矩形 // movingRect = new QWidget(animationWidget); // movingRect->setStyleSheet("background-color: #4CAF50;"); // movingRect->setFixedSize(60, 10); // movingRect->move(0, 10); // //设置动 // animation = new QPropertyAnimation(movingRect,"pos",this); // animation->setDuration(300000); //通过时间 // animation->setStartValue(QPoint(0,10)); // animation->setEndValue(QPoint(1400, 10)); //间距 // animation->setEasingCurve(QEasingCurve::Linear); // animation->setLoopCount(-1); // vLayout->addWidget(animationWidget,1); //创建下层温区 QGroupBox* lowerGroup = new QGroupBox("下层温区"); QHBoxLayout* lowerLayout = new QHBoxLayout(); lowerLayout->setSpacing(15); for(int i = 0;i < 10;++i) { TemperatureGauge* gauge = new TemperatureGauge(); gauge->setFixedSize(60,150); lowerLayout->addWidget(gauge); lowGauges.append(gauge); } lowerGroup->setLayout(lowerLayout); vLayout->addWidget(lowerGroup,2); //添加条形图 QGroupBox* barChartGroup = new QGroupBox("生产数量统计(半小时)"); barChartGroup->setStyleSheet("font:600 12pt '宋体';"); QVBoxLayout* barChartLayout = new QVBoxLayout(barChartGroup); //创建条形图 barChartView = new QChartView(this); barChartView->setRenderHint(QPainter::Antialiasing); barChartLayout->addWidget(barChartView); vLayout->addWidget(barChartGroup,4); //添加主布局 QVBoxLayout *mainLayout = qobject_cast<QVBoxLayout*>(this->layout()); if(mainLayout) { mainLayout->insertLayout(0,vLayout); } createBarChart(); } void JTR::refreshData() { if(!DatabaseManager::instance().isConnected()) { qDebug() << "数据库未连接"; return; } //刷新温度数据 refreshTemperatureData(); //刷新日志数据 refreshLogData(); } void JTR::refreshLogData() { QSqlQuery query(DatabaseManager::instance().getDatabase()); bool querySuccess = query.exec("SELECT time, roleE, roleC, content FROM logs WHERE date(time) = CURRENT_DATE ORDER BY time ASC"); if(!querySuccess) { qDebug() << "日志查询失败:" << query.lastError().text(); return; } QString logText; while(query.next()) { QDateTime time = query.value(0).toDateTime(); QString roleE = query.value(1).toString(); QString roleC = query.value(2).toString(); QString content = query.value(3).toString(); //格式化日志行 logText += QString("%1\t%2\t%3\t%4\n") .arg(time.toString("yyyy-MM-dd hh:mm:ss")) .arg(roleE, -8) //左对齐,占8字符宽度 .arg(roleC, -6) //左对齐,占6字符宽度 .arg(content); } //只有当内容变化时才更新,避免频繁刷新 if(logDisplay->toPlainText() != logText) { logDisplay->setPlainText(logText); logDisplay->moveCursor(QTextCursor::Start); //滚动到顶部 } } // void JTR::refreshTemperatureData() // { // //获取最新的20条记录 // QSqlQuery query(DatabaseManager::instance().getDatabase()); // bool querySuccess = query.exec("SELECT zone_name, zone_level, actual_value, record_time FROM temperature_data " // "WHERE zone_level IN ('上层', '下层') " // "ORDER BY record_time DESC LIMIT 20"); // if(!querySuccess) // { // qDebug() << "查询失败:" << query.lastError().text(); // return; // } // //初始化温度数组 // QVector<float> upTemps(10,0.0f); // QVector<float> lowTemps(10,0.0f); // bool needUpdate = false; // //QDateTime latestTime; // while(query.next()) // { // QString zoneName = query.value(0).toString(); // QString zoneLevel = query.value(1).toString(); // float temp = query.value(2).toFloat(); // //解析温区位置和编号 // QRegularExpression re("(\\d+)"); // QRegularExpressionMatch match = re.match(zoneName); // if(match.hasMatch()) // { // int zoneNum = match.captured(1).toInt(); // if(zoneNum >= 1 && zoneNum <= 10) // { // if(zoneLevel == "上层") // { // if(upTemps[zoneNum - 1] == 0.0f) // 只取第一个有效值 // { // upTemps[zoneNum - 1] = temp; // // 检查是否需要更新 // if(lastUpTemps.size() > zoneNum - 1 && // qAbs(lastUpTemps[zoneNum - 1] - temp) > 0.1f) // { // needUpdate = true; // } // } // } // else if(zoneLevel == "下层") // { // if(lowTemps[zoneNum - 1] == 0.0f) // 只取第一个有效值 // { // lowTemps[zoneNum - 1] = temp; // // 检查是否需要更新 // if(lastLowTemps.size() > zoneNum - 1 && // qAbs(lastLowTemps[zoneNum - 1] - temp) > 0.1f) // { // needUpdate = true; // } // } // } // } // } // } // // 如果没有变化且是第一次更新,则执行更新 // if(!needUpdate && !lastUpTemps.isEmpty()) // { // return; // } // // 更新显示 - 使用直接设置方法避免闪烁 // for(int i = 0; i < upGauges.size() && i < upTemps.size(); ++i) // { // if(lastUpTemps.size() <= i || qAbs(lastUpTemps[i] - upTemps[i]) > 0.1f) // { // upGauges[i]->setTemperatureDirect(upTemps[i]); // } // } // for(int i = 0; i < lowGauges.size() && i < lowTemps.size(); ++i) // { // if(lastLowTemps.size() <= i || qAbs(lastLowTemps[i] - lowTemps[i]) > 0.1f) // { // lowGauges[i]->setTemperatureDirect(lowTemps[i]); // } // } // // 保存当前温度值 // lastUpTemps = upTemps; // lastLowTemps = lowTemps; // } void JTR::refreshTemperatureData() { if(!DatabaseManager::instance().isConnected()) { qDebug() << "数据库未连接"; return; } // 初始化温度数组,使用上一次的值或0 QVector<float> upTemps = lastUpTemps.isEmpty() ? QVector<float>(10, 0.0f) : lastUpTemps; QVector<float> lowTemps = lastLowTemps.isEmpty() ? QVector<float>(10, 0.0f) : lastLowTemps; bool upUpdated = false; bool lowUpdated = false; // 获取最新的温度数据(扩大查询范围确保能获取到所有温区数据) QSqlQuery query(DatabaseManager::instance().getDatabase()); bool querySuccess = query.exec("SELECT zone_name, zone_level, actual_value, record_time FROM temperature_data " "WHERE zone_level IN ('上层', '下层') " "ORDER BY record_time DESC LIMIT 40"); // 增加查询数量 if(!querySuccess) { qDebug() << "温度查询失败:" << query.lastError().text(); return; } // 使用QSet记录已经处理过的温区,避免重复处理 QSet<QString> processedZones; while(query.next()) { QString zoneName = query.value(0).toString(); QString zoneLevel = query.value(1).toString(); float temp = query.value(2).toFloat(); QString zoneKey = zoneLevel + zoneName; // 创建唯一键 // 如果已经处理过这个温区,跳过 if(processedZones.contains(zoneKey)) continue; processedZones.insert(zoneKey); // 解析温区编号 QRegularExpression re("(\\d+)"); QRegularExpressionMatch match = re.match(zoneName); if(!match.hasMatch()) continue; int zoneNum = match.captured(1).toInt(); if(zoneNum < 1 || zoneNum > 10) continue; if(zoneLevel == "上层") { if(upTemps[zoneNum - 1] != temp) { upTemps[zoneNum - 1] = temp; upUpdated = true; } } else if(zoneLevel == "下层") { if(lowTemps[zoneNum - 1] != temp) { lowTemps[zoneNum - 1] = temp; lowUpdated = true; } } } // 更新上层温区显示 if(upUpdated || lastUpTemps.isEmpty()) { for(int i = 0; i < qMin(upGauges.size(), upTemps.size()); ++i) { upGauges[i]->setTemperatureDirect(upTemps[i]); } } // 更新下层温区显示 if(lowUpdated || lastLowTemps.isEmpty()) { for(int i = 0; i < qMin(lowGauges.size(), lowTemps.size()); ++i) { lowGauges[i]->setTemperatureDirect(lowTemps[i]); } } // 保存当前温度值 lastUpTemps = upTemps; lastLowTemps = lowTemps; qDebug() << "温度更新完成 - 上层:" << upTemps << "下层:" << lowTemps; } void JTR::refreshBoardCount() { if(!DatabaseManager::instance().isConnected()) { qDebug() << "数据库未连接"; return; } QSqlQuery query(DatabaseManager::instance().getDatabase()); bool querySuccess = query.exec( "SELECT board_out_count, record_time FROM board_count " "ORDER BY record_time DESC LIMIT 1" ); if(!querySuccess) { qDebug() << "出板数查询失败:" << query.lastError().text(); return; } if(query.next()) { int count = query.value(0).toInt(); QDateTime recordTime = query.value(1).toDateTime(); // 只更新比当前显示更新的数据 if(!lastBoardCountTime.isValid() || recordTime > lastBoardCountTime) { boardCountDisplay->setPlainText(QString::number(count)); lastBoardCountTime = recordTime; // 出板数更新后也更新条形图 updateBarChart(); qDebug() << "更新出板数显示:" << count << "时间:" << recordTime.toString(); } }else { boardCountDisplay->setPlainText("0"); } } 现在这个更新条形图的时候会闪烁更新,可能是因为每次更新会把原来的图像清除掉,然后显示新的数据图像,但是我需要是这样的,我需要的是在原来的图像的基础上变化为新的图像,这样看起来比较流畅
07-18
Qt框架中进行视频处理时,绘制矩形框通常涉及以下几个步骤:获取视频帧、在帧上绘制图形以及将结果渲染到界面上。根据同的视频处理方式(如使用QVideoWidget、QOpenGLWidget或自定义的QWidget),实现细节可能会有所同。 ### 在Qt绘制矩形框的基本方法 假设使用`QPainter`和`QWidget`进行绘制,可以通过以下流程实现在视频帧上叠加矩形框: 1. **捕获视频帧** 如果使用OpenCV,则可以通过`cv::VideoCapture`读取每一帧,并将其转换为`QImage`格式以供Qt界面使用。 2. **绘制矩形框** 利用`QPainter`类提供的绘图功能,在视频帧上绘制矩形框。可以设置笔颜色、线宽等属性。 3. **更新界面显示** 将带有矩形框的图像通过`QLabel`、`QGraphicsView`或其他控件显示出来。 ### 示例代码 以下是一个简单的示例,展示如何在从摄像头捕获的视频帧上绘制矩形框: ```cpp #include <QApplication> #include <QLabel> #include <QTimer> #include <QPainter> #include <opencv2/opencv.hpp> class VideoDisplay : public QLabel { Q_OBJECT public: VideoDisplay(QWidget *parent = nullptr) : QLabel(parent), cap(0) { if (!cap.isOpened()) { setText("无法打开摄像头"); return; } QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &VideoDisplay::updateFrame); timer->start(30); // 每30毫秒更新一帧 } protected: void paintEvent(QPaintEvent *event) override { QLabel::paintEvent(event); if (currentImage.isNull()) return; QPainter painter(this); painter.drawImage(rect(), currentImage); // 绘制矩形框 painter.setPen(QPen(Qt::red, 2)); painter.drawRect(rectangle); // rectangle 是 QRect 类型 } private slots: void updateFrame() { cv::Mat frame; cap >> frame; if (frame.empty()) return; // 将 OpenCV Mat 转换为 QImage QImage qImg(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_BGR888); currentImage = qImg.copy(); // 保存当前帧 // 设置要绘制的矩形区域 rectangle = QRect(50, 50, 200, 100); // 示例矩形位置和大小 update(); // 触发重绘事件 } private: cv::VideoCapture cap; QImage currentImage; QRect rectangle; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); VideoDisplay display; display.resize(640, 480); display.show(); return app.exec(); } #include "main.moc" ``` ### 使用QML进行视频绘制 如果采用QML作为前端界面,结合C++后端处理视频流,则可以使用`QQuickPaintedItem`或`ShaderEffect`来绘制矩形框。这种方式更适用于需要高度定制化UI的场景。 ### 性能优化建议 - 对于高分辨率或高速视频流,推荐使用GPU加速,例如结合CUDA或OpenCL进行图像处理[^3]。 - 若需实时性要求较高,可考虑使用多线程架构,分离视频采集与图像处理逻辑。 - 可以利用Qt的信号槽机制异步更新UI,避免阻塞主线程。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值