1. 程式人生 > 其它 >Qt編寫視覺化大屏電子看板系統27-模組5負荷分佈

Qt編寫視覺化大屏電子看板系統27-模組5負荷分佈

一、前言

負荷分佈模組包括工序計劃負荷、當日負荷、負荷百分比三個子模組,工序計劃負荷用表格的形式展示不同工序在不同日期的負荷工作時長,比如組裝工序在 2022-02-10 運行了88小時,一般表格顯示最近的一星期的資訊;當日負荷用柱狀圖的形式顯示不同工序當天的負荷執行時長;負荷百分比用折線圖展示,其中包括了定位線預設放在100的值位置。

子模組表名對應表:

子模組標題 子模組表名
工序計劃負荷 t_5_1_work_load
當日負荷 t_5_2_work_load_today
負荷百分比 t_5_3_work_load_percent

1 工序計劃負荷

表名:t_5_1_work_load

欄位名 中文名 型別 長度 說明
internal_id 序號 INTEGER 11 主鍵自增
process_name 工序名稱 VARCHAR 255
work_load_1 第1天負荷 VARCHAR 255
work_load_* 第*天負荷 VARCHAR 255
work_load_7 第7天負荷 VARCHAR 255

預設資料:

internal_id process_name work_load_1 work_load_2 work_load_3 work_load_4 work_load_5 work_load_6 work_load_7
1 CNC粗 101H 81H 90H 120H 30H 60H 120H
2 CNC精 102H 102H 120H 81H 45H 102H 81H
3 EDM 77H 102H 90H 102H 45H 90H 120H
4 WEDM 87H 102H 120H 45H 102H 102H 90H
5 拋光 45H 102H 102H 90H 81H 81H 81H
6 鉗工 89H 90H 45H 120H 120H 120H 102H
7 組裝 77H 90H 81H 102H 45H 102H 81H

表名:t_5_1_work_load_table_head
這個設計很巧妙,相當於動態的日期和資料,日期作為標題。

欄位名 中文名 型別 長度 說明
internal_id 序號 INTEGER 11 主鍵自增
date_1 第1天日期 VARCHAR 255
date_2 第2天日期 VARCHAR 255
date_3 第3天日期 VARCHAR 255
date_4 第4天日期 VARCHAR 255
date_5 第5天日期 VARCHAR 255
date_6 第6天日期 VARCHAR 255
date_7 第7天日期 VARCHAR 255

預設資料:

internal_id date_1 date_2 date_3 date_4 date_5 date_6 date_7
1 2019-04-30 2019-05-01 2019-05-02 2019-05-03 2019-05-04 2019-05-05 2019-05-06

2 當日負荷

表名:t_5_2_work_load_today

欄位名 中文名 型別 長度 說明
internal_id 序號 INTEGER 11 主鍵自增
group_name 分組名稱 REAL 11 小數位精度1
green 綠色值 REAL 11 小數位精度1
red 紅色值 REAL 11 小數位精度1

預設資料:

internal_id group_name green red
1 CNC粗 20 20
2 CNC精 40 20
3 EDM 60 0
4 WEDM 30.2 2
5 銑床 40 0
6 磨床 30 50

3 負荷百分比

表名:t_5_3_work_load_percent

欄位名 中文名 型別 長度 說明
internal_id 序號 INTEGER 11 主鍵自增
group_name 分組名稱 VARCHAR 255
day_1 第1天 INTEGER 3
day_* 第*天 INTEGER 3
day_7 第7天 INTEGER 3

預設資料:

internal_id group_name day_1 day_2 day_3 day_4 day_5 day_6 day_7
1 CNC粗 70 80 90 70 50 99 80
2 CNC精 120 100 130 140 90 100 85
3 EDM 120 100 120 80 90 40 50
4 WEDM 100 120 120 100 100 80 70
5 銑床 90 80 75 40 12 30 10
6 磨床 80 70 50 60 40 50 30

二、功能特點

  1. 採用分層設計,整體總共分三級介面,一級介面是整體佈局,二級介面是單個功能模組,三級介面是單個控制元件。
  2. 子控制元件包括餅圖、圓環圖、曲線圖、柱狀圖、柱狀分組圖、橫向柱狀圖、橫向柱狀分組圖、合格率控制元件、百分比控制元件、進度控制元件、裝置狀態面板、表格資料、地圖控制元件、視訊控制元件等。
  3. 二級介面可以自由拖動懸浮,支援最小化隱藏、最大化關閉、響應雙擊自定義標題欄。
  4. 資料來源支援模擬資料(預設)、資料庫採集、串列埠通訊(需定製)、網路通訊(需定製)、網路請求等,可自由設定每個子介面的採集間隔即資料重新整理頻率。
  5. 採用純QWidget編寫,親測Qt4.6到Qt6.2任意版本,理論上支援後續其他Qt版本。
  6. 超強跨平臺,親測windows、linux、mac、國產uos、國產銀河麒麟kylin等系統,效果完美,同時還支援嵌入式linux比如樹莓派、香橙派、全志、imx6等。
  7. 同時集成了自定義控制元件、qchart餅圖、echart地圖等功能。
  8. 內建多套配色風格樣式(紫色、藍色、深藍、黑色),預設紫色,自適應任意解析度。
  9. 可設定系統標題、目標解析度、佈局方案,啟動立即應用。
  10. 可設定主背景顏色、面板顏色、十字線遊標顏色等各種顏色。
  11. 可設定多條曲線不同顏色,沒有設定顏色的情況下內建多套精美顏色隨機應用。
  12. 可設定標題欄背景顏色、文字顏色。
  13. 可設定曲線圖表背景顏色、文字顏色、網格顏色。
  14. 可設定正常顏色、警戒顏色、報警顏色、禁用顏色、百分比進度顏色。
  15. 可分別設定各種字型大小,比如全域性字型、軟體名稱、標題欄、子標題欄、加粗標籤等。
  16. 可設定標題欄高度、表頭高度、行高度。
  17. 曲線支援遊標、定位線、懸停高亮資料點、懸停顯示值。
  18. 柱狀圖支援頂部(可設定頂端、上部、中間、底部)顯示資料,全部自適應計算位置。
  19. 支援平滑曲線,內建多種平滑曲線演算法,還支援面積圖平滑。
  20. 面積圖填充顏色可選多種規則比如單色透明度填充、透明度漸變填充等。
  21. 資料庫支援sqlite、mysql、postgresql、oracle、國產人大金倉等資料庫。
  22. 主介面直接滑鼠右鍵切換佈局、配色方案、關閉開啟某個二級窗體。
  23. 自動記憶所有子視窗的大小和位置,下次啟動立即應用。
  24. 動態載入佈局方案選單,可以動態新建佈局、恢復佈局、儲存佈局、另存佈局等,使用者可以製造任意佈局。
  25. 二級窗體,雙擊從主窗體分離出來浮動,可以自由調整大小。再次雙擊標題欄最大化,再次雙擊還原。
  26. 子模組也可以全屏顯示作為一個大屏,這樣就可以一個大屏拓展出多個子大屏,放大檢視子模組的資料詳情,適用多屏展示。
  27. 每個模組都可以自定義採集速度,如果是資料庫採集會自動排隊處理,後期還可以拓展每個子模組都獨立的資料庫採集。
  28. 提供系統設定模組進行整體的配置引數設定,效果立即應用。
  29. 提供精美炫酷的大屏地圖模組,包括靜態圖片、閃爍效果、遷徙效果、世界地圖、區域地圖等,可指定點的經緯度座標,識別單擊響應,可以做地圖跳轉等,每個點都可以不同的顏色和提示資訊。
  30. 除了提供大屏系統外,還將每個模組都做了獨立的模組示例介面,每個模組都可以獨立學習使用,裡面用到的控制元件也單獨做了控制元件示例介面,方便學習每個控制元件如何使用。
  31. 非常詳細的開發和使用手冊,其中包括資料庫說明、模組對照圖、控制元件對照圖、專案結構、程式碼說明(精確到每個類)、演示demo、使用方法等。

三、體驗地址

  1. 體驗地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 檔名:bin_bigscreen.zip。
  2. 國內站點:https://gitee.com/feiyangqingyun
  3. 國際站點:https://github.com/feiyangqingyun
  4. 個人主頁:https://blog.csdn.net/feiyangqingyun
  5. 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/
  6. 線上文件:https://feiyangqingyun.gitee.io/qwidgetdemo/bigscreen/

四、效果圖

五、核心程式碼

#include "frmmodule5.h"
#include "ui_frmmodule5.h"
#include "quihelper.h"
#include "progressthree.h"

frmModule5::frmModule5(QWidget *parent) : QWidget(parent), ui(new Ui::frmModule5)
{
    ui->setupUi(this);
    this->initForm();
    this->initTable();
    this->initPlot1();
    this->initPlot2();
}

frmModule5::~frmModule5()
{
    delete ui;
}

void frmModule5::initForm()
{
    //設定對應的屬性應用特定樣式
    ui->labTitle1->setProperty("flag", "title");
    ui->labTitle2->setProperty("flag", "title");
    ui->labTitle3->setProperty("flag", "title");

    ui->widgetSub1->setProperty("flag", "sub");
    ui->widgetSub2->setProperty("flag", "sub");
    ui->widgetSub3->setProperty("flag", "sub");

    //定時器模擬資料
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(loadTable()));
    connect(timer, SIGNAL(timeout()), this, SLOT(loadPlot1()));
    connect(timer, SIGNAL(timeout()), this, SLOT(loadPlot2()));

    //繫結訊號槽處理接收的資料+傳送執查詢語句
    connect(DbData::DbLocal, SIGNAL(receiveData(QString, QStringList, int)),
            this, SLOT(receiveData(QString, QStringList, int)));
    connect(DbData::DbHttp, SIGNAL(receiveData(QString, QStringList, int)),
            this, SLOT(receiveData(QString, QStringList, int)));

    //繫結樣式改變訊號重新載入資料
    connect(AppEvent::Instance(), SIGNAL(changeStyle()), this, SLOT(start()));
}

void frmModule5::receiveData(const QString &tag, const QStringList &data, int mesc)
{
    int count = data.count();
    if (tag == "t_5_1_work_load") {
        if (count % 8 == 0) {
            lstringl values;
            ui->tableWidget->setRowCount(count / 8);
            for (int i = 0; i < count; i = i + 8) {
                QStringList value;
                value << data.at(i);
                value << data.at(i + 1);
                value << data.at(i + 2);
                value << data.at(i + 3);
                value << data.at(i + 4);
                value << data.at(i + 5);
                value << data.at(i + 6);
                value << data.at(i + 7);
                value << QString("100|0|0");
                values << value;
            }

            loadTable(values);
        }
    } else if (tag == "t_5_2_work_load_today") {
        if (count % 3 == 0) {
            rowNames1.clear();
            vdouble value1, value2;
            for (int i = 0; i < count; i = i + 3) {
                rowNames1 << data.at(i);
                value1 << data.at(i + 1).toDouble();
                value2 << data.at(i + 2).toDouble();
            }

            loadPlot1(lvdouble() << value1 << value2);
        }
    } else if (tag == "t_5_3_work_load_percent") {
        if (count % 8 == 0) {
            lineNames.clear();
            vstring lab;
            lab << "1" << "2" << "3" << "4" << "5" << "6" << "7";
            vdouble key;
            key << 0 << 1 << 2 << 3 << 4 << 5 << 6;

            //取出總共行數
            int row = count / 8;
            lvdouble values;
            for (int i = 0; i < row; i++) {
                for (int j = (8 * i); j < (8 * i) + 1; j++) {
                    lineNames << data.at(j);
                }

                double max = 8 * (i + 1);
                vdouble value;
                for (int j = (8 * i) + 1; j < max; j++) {
                    value << data.at(j).toDouble();
                }

                values << value;
            }

            loadPlot2(lab, key, values);
        }
    } else if (tag == "t_5_1_work_load_table_head") {
        if (count == 7) {
            QStringList headText;
            headText << "工序";
            for (int i = 0; i < count; i++) {
                headText << data.at(i);
            }

            ui->tableWidget->setHorizontalHeaderLabels(headText);
        }
    }
}

void frmModule5::initTable()
{
    QStringList headText;
    headText << "工序" << "4月1日" << "4月2日" << "4月3日" << "4月4日" << "4月5日" << "4月6日" << "4月7日" << "進度" << "";

    int columnCount = headText.count();
    ui->tableWidget->setColumnCount(columnCount);
    ui->tableWidget->setHorizontalHeaderLabels(headText);
    ui->tableWidget->horizontalHeader()->setMinimumHeight(AppConfig::HeadHeight);
    ui->tableWidget->verticalHeader()->setDefaultSectionSize(AppConfig::RowHeight);

    //計算值的寬度
    int width = 100;
    if (QUIHelper::deskWidth() <= 1440) {
        width = 80;
    }

    if (AppConfig::WorkMode != "timer") {
        width = 110;
    }

    ui->tableWidget->setColumnWidth(0, 80);
    ui->tableWidget->setColumnWidth(columnCount - 2, 120);
    ui->tableWidget->setColumnHidden(columnCount - 2, true);
    for (int i = 1; i < columnCount - 1; i++) {
        ui->tableWidget->setColumnWidth(i, width);
    }

    ui->tableWidget->setFocusPolicy(Qt::NoFocus);
    ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    ui->tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    //ui->tableWidget->setAlternatingRowColors(true);
    ui->tableWidget->verticalHeader()->setVisible(false);
    ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
    ui->tableWidget->horizontalHeader()->setHighlightSections(false);
}

void frmModule5::loadTable()
{
    QStringList list;
    list << "process_name";
    for (int i = 1; i <= 7; i++) {
        list << QString("work_load_%1").arg(i);
    }

    QString tableName = "t_5_1_work_load";
    QString columnName = list.join(",");

    //讀取表頭
    list.clear();
    for (int i = 1; i <= 7; i++) {
        list << QString("date_%1").arg(i);
    }

    QString tableName2 = "t_5_1_work_load_table_head";
    QString columnName2 = list.join(",");

    if (AppConfig::WorkMode == "timer") {
        lstringl values;
        int rowCount = 9;
        ui->tableWidget->setRowCount(rowCount);
        for (int i = 0; i < rowCount; i++) {
            QStringList value;
            value << QString(QUIHelper::getRandValue(1, 3) == 1 ? "CNC粗" : "CNC精");
            value << QString("%1H").arg(QUIHelper::getRandValue(50, 150));
            value << QString("%1H").arg(QUIHelper::getRandValue(100, 150));
            value << QString("%1H").arg(QUIHelper::getRandValue(50, 150));
            value << QString("%1H").arg(QUIHelper::getRandValue(100, 150));
            value << QString("%1H").arg(QUIHelper::getRandValue(80, 150));
            value << QString("%1H").arg(QUIHelper::getRandValue(100, 150));
            value << QString("%1H").arg(QUIHelper::getRandValue(100, 150));
            value << QString("%1|%2|0").arg(QUIHelper::getRandValue(10, 50)).arg(QUIHelper::getRandValue(10, 50));
            values << value;
        }

        loadTable(values);
    } else if (AppConfig::WorkMode == "db") {
        DbData::DbLocal->select(tableName, columnName, true);
        DbData::DbLocal->select(tableName2, columnName2, true);
    } else if (AppConfig::WorkMode == "http") {
        DbData::DbHttp->select(tableName, columnName, true);
        DbData::DbHttp->select(tableName2, columnName2, true);
    }
}

void frmModule5::loadTable(const lstringl &values)
{
    int rowCount = values.count();
    for (int i = 0; i < rowCount; i++) {
        QStringList value = values.at(i);
        int columnCount = value.count();
        for (int j = 0; j < columnCount; j++) {
            //末尾資料為進度條
            if (j == columnCount - 1) {
                QString data = value.at(j);
                QStringList list = data.split("|");
                if (list.count() != 3) {
                    continue;
                }

                //自定義多型進度條
                ProgressThree *progress = new ProgressThree;
                progress->setValue1(list.at(0).toInt());
                progress->setValue2(list.at(1).toInt());
                progress->setValue3(list.at(2).toInt());
                progress->setFixedHeight(15);

                //增加widget+佈局巧妙實現居中
                QWidget *widget = new QWidget;
                QHBoxLayout *layout = new QHBoxLayout;
                layout->setSpacing(0);
                layout->setContentsMargins(0, 0, 0, 0);
                layout->addWidget(progress);
                widget->setLayout(layout);
                ui->tableWidget->setCellWidget(i, j, widget);
            } else {
                QTableWidgetItem *item = new QTableWidgetItem(value.at(j));
                item->setTextAlignment(Qt::AlignCenter);
                ui->tableWidget->setItem(i, j, item);
            }
        }
    }
}

void frmModule5::initPlot1()
{
    rowNames1 << "CNC粗" << "CNC精" << "EDM" << "WEDM" << "銑床" << "磨床";
    columnNames1 << "系列1" << "系列2";
}

void frmModule5::loadPlot1()
{
    QString tableName = "t_5_2_work_load_today";
    QString columnName = "group_name,green,red";

    if (AppConfig::WorkMode == "timer") {
        vdouble value1, value2;
        for (int i = 0; i < 6; i++) {
            value1 << QUIHelper::getRandValue(20, 50);
            value2 << QUIHelper::getRandValue(20, 50);
        }

        loadPlot1(lvdouble() << value1 << value2);
    } else if (AppConfig::WorkMode == "db") {
        DbData::DbLocal->select(tableName, columnName, true);
    } else if (AppConfig::WorkMode == "http") {
        DbData::DbHttp->select(tableName, columnName, true);
    }
}

void frmModule5::loadPlot1(const lvdouble &values)
{
    ui->customPlot1->clear();
    double maxY = CustomPlotHelper::getMaxValue2(values) * 1.2;
    ui->customPlot1->setRangeX(0.5, rowNames1.count() + 0.5);
    ui->customPlot1->setRangeY(0, maxY, AppConfig::StepY);

    //設定柱狀資料結構體
    BarData data;
    data.rowNames = rowNames1;
    data.columnNames = columnNames1;
    data.values = values;
    data.borderColor = AppConfig::ColorChartText;
    data.valuePosition = 0;
    data.valuePrecision = 0;
    data.valueColor = AppConfig::ColorChartText;
    ui->customPlot1->setDataBars(data);
    ui->customPlot1->replot();
}

void frmModule5::initPlot2()
{
    lineNames << "CNC粗" << "CNC精" << "EDM" << "WEDM" << "銑床" << "磨床";
    ui->customPlot2->setLegend(true, 7, 3);
}

void frmModule5::loadPlot2()
{
    QStringList list;
    list << "group_name";
    for (int i = 1; i <= 7; i++) {
        list << QString("day_%1").arg(i);
    }

    QString tableName = "t_5_3_work_load_percent";
    QString columnName = list.join(",");

    if (AppConfig::WorkMode == "timer") {
        vstring lab;
        lab << "1" << "2" << "3" << "4" << "5" << "6" << "7";
        vdouble key, value1, value2, value3, value4, value5, value6;
        key << 0 << 1 << 2 << 3 << 4 << 5 << 6;

        int dataCount = lab.count();
        for (int i = 0; i < dataCount; i++) {
            value1 << QUIHelper::getRandValue(10, 150);
            value2 << QUIHelper::getRandValue(10, 150);
            value3 << QUIHelper::getRandValue(10, 150);
            value4 << QUIHelper::getRandValue(10, 150);
            value5 << QUIHelper::getRandValue(10, 150);
            value6 << QUIHelper::getRandValue(10, 150);
        }

        loadPlot2(lab, key, lvdouble() << value1 << value2 << value3 << value4 << value5 << value6);
    } else if (AppConfig::WorkMode == "db") {
        DbData::DbLocal->select(tableName, columnName, true);
    } else if (AppConfig::WorkMode == "http") {
        DbData::DbHttp->select(tableName, columnName, true);
    }
}

void frmModule5::loadPlot2(const vstring &lab, const vdouble &key, const lvdouble &values)
{
    if (AppConfig::StaticLine) {
        ui->customPlot2->drawStaticLineh(-0.5, 100, AppConfig::ColorLine);
    }

    int count = values.count();
    ui->customPlot2->getPlot()->clearGraphs();
    ui->customPlot2->addGraph(count);

    LineData data;
    data.key = key;
    for (int i = 0; i < count; i++) {
        data.index = i;
        data.name = lineNames.at(i);
        data.value = values.at(i);
        //設定線條顏色,可以指定顏色也可以隨機顏色
        data.lineColor = ui->customPlot2->getColors().at(i);
        ui->customPlot2->setDataLine(data);
    }

    //設定座標軸範圍值+X座標對應顯示文字
    double maxY = CustomPlotHelper::getMaxValue(values) * 1.5;
    ui->customPlot2->setLabX(key, lab);
    ui->customPlot2->setRangeX(-0.5, key.count() - 0.5);
    ui->customPlot2->setRangeY(0, maxY, AppConfig::StepY, AppConfig::ShowPercent);
    ui->customPlot2->replot();
}

void frmModule5::start(int interval)
{
    this->loadPlot1();
    this->loadPlot2();
    this->loadTable();

    //如果間隔太短表示不需要重新整理,執行一次即可
    if (interval > 2000) {
        timer->start(interval);
    }
}

void frmModule5::stop()
{
    if (timer->isActive()) {
        timer->stop();
    }
}