Qt編寫資料視覺化大屏介面電子看板1-佈局方案
一、前言
佈局方案在整個資料視覺化大屏介面電子看板系統中,是除了基礎功能以外的核心功能之一,只有具備了佈局方案這個功能,才能讓使用者隨意調整自己想要的佈局,儲存成自定義名稱的佈局配置檔案,這樣就大大增加了靈活性,可以更好的適應各種解析度,畢竟客戶的電腦執行環境各種各樣的都有,模組數量眾多,有些不想展示,有些需要特別放大展示,有些需要偶爾全屏展示等,這些佈局使用者都可以自己定義好儲存配置方案,儲存到指定的目錄下,下次啟動會自動生成對應的佈局選單檔案讓使用者自行選擇切換。
二、電子看板介紹
電子看板是目視化管理的一種表現形式,即對資料的狀況一目瞭然地表現,主要是對於管理專案,它通過利用形象直觀而又色彩適宜的各種視覺感知資訊來組織現場生產活動,目視管理依據人類的生理特徵,在生產現場充分利用訊號燈、標識牌、符號顏色等方式來發出視覺訊號,鮮明準確地刺激人的神經末梢,快速地傳遞資訊,形象直觀地將潛在的問題和浪費現象都顯現出來。以便任何人都可以及時掌握管理現狀和必要的情報,從而能夠快速制定並實施應對措施。因此,管理看板是發現問題、解決問題的非常有效且直觀的手段,是優秀的現場管理必不可少的工具之一。
三、功能特點
1:整體總共分三級介面,一級介面是整體佈局,二級介面是單個功能模組,三級介面是單個控制元件。 2:子控制元件包括餅圖+圓環圖+曲線圖+柱狀圖+柱狀分組圖+橫向柱狀圖+橫向柱狀分組圖+合格率控制元件+百分比控制元件+進度控制元件+裝置狀態面板+表格資料+地圖控制元件+視訊控制元件+其他控制元件等。 3:二級介面可以自由拖動懸浮,支援最小化最大化關閉,響應雙擊自定義標題欄。 4:資料來源支援資料庫採集(預設)、網路通訊、網路請求等,可自由設定每個子介面的採集間隔即資料重新整理頻率。 5:採用純QWidget編寫,支援Qt4.6到Qt5.12.3任何版本,支援嵌入式linux比如樹莓派、香橙派、全志、imx6等。 6:提供三個核心版本,自定義控制元件版本+qchart版本+echart版本。 7:內建多套配色風格樣式,預設紫色,支援任何解析度。 8:可設定標題+目標解析度+佈局方案,啟動立即應用。 9:可設定主背景顏色+面板顏色+十字線遊標顏色。 10:可設定多條曲線顏色,沒有設定顏色的情況下內建15套精美顏色隨機應用。 11:可設定標題欄背景顏色+文字顏色。 12:可設定曲線圖表背景顏色+文字顏色+網格顏色。 13:可設定正常顏色+警戒顏色+報警顏色+禁用顏色+百分比進度顏色。 14:可分別設定各種字型大小,比如全域性+軟體名稱+標題欄+子標題欄+加粗標籤等。 15:可設定標題欄高度+表頭高度+行高度。 16:曲線支援遊標+懸停高亮資料點和顯示值,柱狀圖支援頂部(可設定頂端+上部+中間+底部)顯示資料,全部自適應計算位置。 17:主介面直接滑鼠右鍵切換佈局+配色方案+關閉開啟某個二級窗體。 18:自動記憶所有子視窗的大小和位置,下次啟動立即應用。 19:動態載入佈局方案選單,可以動態新建佈局、恢復佈局、儲存佈局、另存佈局等,使用者可以製造任意佈局。 20:二級窗體,雙擊從主窗體分離出來浮動,可以自由調整大小。再次雙擊標題欄最大化,再次雙擊還原。 21:每個模組都可以自定義採集速度,如果是資料庫採集會自動排隊處理。
四、配置檔案說明
(1)、基本配置引數
欄位 | 描述 | 預設值 |
---|---|---|
WorkMode | 工作模式 timer-模擬資料 db-資料庫採集 tcp-網路採集 http-post請求 | timer |
Title | 軟體標題,顯示在軟體中間頂部 | 數字化工廠資訊中心 |
Ratio | 解析度,目前無意義 | 4096*216 |
Layout | 佈局方案,每次切換佈局方案以後都會儲存 | 完整佈局 |
Theme | 配色方案,每次切換配色方案以後都會儲存 | 紫色風格 |
VideoAddr | 視訊流地址,視訊模組播放的視訊地址 | 鳳凰衛視 |
AutoRun | 是否開機啟動 | false |
MoveEnable | 模組是否可以拖動,啟用以後模組可以任意拖動 | true |
CutLeftBottom | 底部佈局左側是否切掉 | true |
CutRightBottom | 底部佈局右側是否切掉 | true |
StaticLine | 是否繪製靜態定位線,為假則繪製遊標十字線 | true |
(2)、顏色配置引數
欄位 | 描述 | 預設值 |
---|---|---|
ColorMainBg | 主背景顏色 | QColor(4, 7, 38) |
ColorPanelBg | 面板背景顏色 | QColor(26, 29, 60) |
ColorLine | 十字線定位線顏色 | QColor(255, 0, 0) |
ColorLine1 | 線條1顏色 | QColor(0, 176, 180) |
ColorLine2 | 線條2顏色 | QColor(32, 159, 223) |
ColorLine3 | 線條3顏色 | QColor(255, 192, 0) |
ColorTitleBg | 標題欄背景顏色 | QColor(48, 48, 85) |
ColorTitleText | 標題欄文字顏色 | QColor(255, 255, 255) |
ColorChartBg | 曲線圖表背景顏色 | QColor(38, 41, 74) |
ColorChartText | 曲線圖表文字顏色 | QColor(250, 250, 250) |
ColorChartGrid | 曲線圖表網格顏色 | QColor(180, 180, 180) |
ColorOk | 正常顏色 | QColor(0, 176, 180) |
ColorLow | 警戒顏色 | QColor(255, 192, 0) |
ColorAlarm | 報警顏色 | QColor(214, 77, 84) |
ColorDisable | 禁用背景顏色 | QColor(210, 210, 210) |
ColorPercent | 環形百分比背景顏色 | QColor(0, 254, 254) |
(3)、字型和尺寸配置引數
欄位 | 描述 | 預設值 |
---|---|---|
MainFont | 全域性字號 | 微軟雅黑,12 |
NameFont | 軟體名稱字號 | 19 |
LabFont | 加粗標籤字號 | 12 |
DeviceFont | 裝置面板字號 | 12 |
SubTitleFont | 模組子標題欄字號 | 13 |
TitleFont | 模組標題欄字號 | 15 |
TitleHeight | 模組標題欄高度 | 23 |
HeadHeight | 表格表頭高度 | 28 |
RowHeight | 表格行高度 | 25 |
(4)、採集速度配置引數,單位毫秒
欄位 | 描述 | 預設值 |
---|---|---|
IntervalModule1 | 模組1採集間隔 | 5000 |
IntervalModule2 | 模組2採集間隔 | 5000 |
IntervalModule3 | 模組3採集間隔 | 5000 |
IntervalModule4 | 模組4採集間隔 | 5000 |
IntervalModule5 | 模組5採集間隔 | 5000 |
IntervalModule6 | 模組6採集間隔 | 5000 |
IntervalModule7 | 模組7採集間隔 | 5000 |
IntervalModule8 | 模組8採集間隔 | 5000 |
(5)、本地資料庫配置引數
欄位 | 描述 | 預設值 |
---|---|---|
LocalDBType | 本地資料庫型別,Sqlite、Mysql等 | Mysql |
LocalDBIP | 本地資料庫主機地址 | 127.0.0.1 |
LocalDBPort | 本地資料庫埠 | 3306 |
LocalDBName | 本地資料庫名稱 | bigscreen |
LocalUserName | 本地資料庫使用者名稱 | root |
LocalUserPwd | 本地資料庫密碼,以密文儲存 | root |
五、特別說明
- 執行檔案同級資料夾有layout+layout_1440+layout_1920,程式預設自動識別解析度並載入對應的佈局資料夾,比如1920解析度則從layout_1920資料夾載入佈局,並作為整體佈局資料夾。
- 程式預設是模擬資料,如果需要從資料庫採集則修改配置檔案WorkMode=db即可。
- 如果發現佈局拖動亂了,可以直接滑鼠右鍵選擇恢復佈局即可,在儲存佈局以前。
- 在中間地圖模組滑鼠右鍵可以彈出選單,切換佈局和配色方案等。
- 在模組的標題欄上右鍵可以彈出預設的dock選單,用來顯示和隱藏各模組。
- 軟體關閉過程中會自動儲存佈局,下次啟動以後自動應用。
- 如果使用的預設的預設的配色方案比如紫色風格,則配置檔案中的顏色全部無效,會自動應用程式碼中的顏色,如果需要啟用自定義的顏色,則將配置檔案的 Theme=\x81ea\x5b9a\x4e49\x98ce\x683c 即可。此時開啟軟體會應用配置檔案中的顏色。
- 右鍵選單可以截圖儲存,預設命名為 配色方案名稱_佈局方案名稱.png 儲存在snap目錄下。
- 如果是XP系統請先執行fixff.cmd,用來修復ffmpeg在XP上不可用的BUG。
- 可執行檔案下載地址:https://pan.baidu.com/s/1o97IGvZgTgDhlkuXQa4B0w 提取碼:r2bv ,會不定期更新程式,歡迎各位提出批評和建議。
六、效果圖
七、核心程式碼
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "quiwidget.h"
#include "appinit.h"
#include "customtitlebar.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->initForm();
this->changeLayout(App::Layout, true);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *)
{
closeAll();
}
void MainWindow::initForm()
{
this->setWindowTitle(App::Title);
//this->setProperty("canMove", true);
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);
//自動根據解析度找合適的預設的佈局資料夾
layoutPath = QUIHelper::appPath() + "/layout";
if (QUIHelper::deskWidth() == 1440) {
QDir dir(QUIHelper::appPath() + "/layout_1440");
if (dir.exists()) {
layoutPath = QUIHelper::appPath() + "/layout_1440";
}
} else if (QUIHelper::deskWidth() == 1920) {
QDir dir(QUIHelper::appPath() + "/layout_1920");
if (dir.exists()) {
layoutPath = QUIHelper::appPath() + "/layout_1920";
}
}
module1 = new frmModule1;
module2 = new frmModule2;
module3 = new frmModule3;
module4 = new frmModule4;
module5 = new frmModule5;
module6 = new frmModule6;
module7 = new frmModule7;
module8 = new frmModule8;
moduleCenter = new frmModuleCenter;
moduleVideo = new frmModuleVideo;
//啟動模擬資料或者資料採集
module1->start(App::IntervalModule1);
module2->start(App::IntervalModule2);
module3->start(App::IntervalModule3);
module4->start(App::IntervalModule4);
module5->start(App::IntervalModule5);
module6->start(App::IntervalModule6);
module7->start(App::IntervalModule7);
module8->start(App::IntervalModule8);
//例項化停靠窗體
newWidget(module1, "年度產量彙總");
newWidget(module2, "當月計劃達成率");
newWidget(module3, "裝置監控");
newWidget(module4, "模具進度");
newWidget(module5, "負荷分佈");
newWidget(module6, "送檢一次合格率");
newWidget(module7, "品質管理");
newWidget(module8, "物料管理");
newWidget(moduleVideo, "視訊監控");
QList<QWidget *> widgets;
widgets << module1 << module2 << module3 << module4 << module5 << module6 << module7 << module8 << moduleCenter << moduleVideo;
connect(moduleCenter, SIGNAL(changeLayout(QString)), this, SLOT(changeLayout(QString)));
connect(moduleCenter, SIGNAL(saveLayout(QString, int)), this, SLOT(saveLayout(QString, int)));
connect(moduleCenter, SIGNAL(changeTheme(QString)), this, SLOT(changeTheme(QString)));
connect(moduleCenter, SIGNAL(closeAll()), this, SLOT(closeAll()));
//設定拉伸策略
for (int i = 0; i < widgets.count(); i++) {
widgets.at(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
}
//設定中心窗體
this->setCentralWidget(moduleCenter);
//設定停靠引數,不允許重疊,只允許拖動
this->setDockOptions(QMainWindow::AnimatedDocks);
//將底部左側作為左側區域,底部右側作為右側區域,否則底部區域會填充拉伸
if (App::CutLeftBottom) {
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
}
if (App::CutRightBottom) {
setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
}
}
void MainWindow::clearWidget()
{
for (int i = 0; i < widgets.count(); i++) {
this->removeDockWidget(widgets.at(i));
}
}
void MainWindow::initWidget()
{
//新增左側窗體
addWidget(widgets.at(0), 0);
addWidget(widgets.at(7), 0);
addWidget(widgets.at(3), 0);
//新增右側窗體
addWidget(widgets.at(6), 1);
addWidget(widgets.at(4), 1);
addWidget(widgets.at(5), 1);
//新增底部窗體
addWidget(widgets.at(2), 3);
addWidget(widgets.at(1), 3);
addWidget(widgets.at(8), 3);
}
void MainWindow::newWidget(QWidget *widget, const QString &title)
{
//自定義停靠窗體標題欄
QString objName = widget->objectName();
CustomTitleBar *titleBar = new CustomTitleBar;
titleBar->setObjectName("titleBar_" + objName);
titleBar->setTitle(title);
//例項化停靠窗體
QDockWidget *dockWidget = new QDockWidget;
dockWidget->setObjectName("dockWidget_" + objName);
dockWidget->setWindowTitle(title);
dockWidget->setTitleBarWidget(titleBar);
dockWidget->setWidget(widget);
//如果設定了不可移動則只允許關閉
if (!App::MoveEnable) {
dockWidget->setFeatures(QDockWidget::DockWidgetClosable);
}
//設定頂部不可停靠
dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::BottomDockWidgetArea);
widgets << dockWidget;
}
void MainWindow::addWidget(QDockWidget *widget, int position)
{
//設定停靠位置
Qt::DockWidgetArea area;
if (position == 0) {
area = Qt::LeftDockWidgetArea;
} else if (position == 1) {
area = Qt::RightDockWidgetArea;
} else if (position == 2) {
area = Qt::TopDockWidgetArea;
} else if (position == 3) {
area = Qt::BottomDockWidgetArea;
}
this->addDockWidget(area, widget);
//如果是首次生成佈局則需要全部可見
QString file = QString("%1/%2.ini").arg(layoutPath).arg(App::Layout);
if (!QFile(file).exists()) {
widget->setVisible(true);
}
}
void MainWindow::changeLayout(const QString &layout, bool init)
{
//首次載入不需要比較是否和配置檔案一樣
bool needLoad = init;
if (!init && App::Layout != layout) {
needLoad = true;
//先儲存原有佈局
saveLayout(App::Layout, 2);
}
if (needLoad) {
App::Layout = layout;
App::writeConfig();
this->clearWidget();
this->initWidget();
this->initLayout(App::Layout);
//全屏+QWebEngineView控制元件一起會產生右鍵選單無法彈出的BUG,需要上移一個畫素
QRect rect = qApp->desktop()->screenGeometry();
#if 1
rect.setY(-1);
rect.setHeight(rect.height());
#else
rect.setX(30);
rect.setY(40);
rect.setWidth(1370);
rect.setHeight(795);
#endif
this->setGeometry(rect);
}
}
void MainWindow::initLayout(const QString &layout)
{
QString file = QString("%1/%2.ini").arg(layoutPath).arg(layout);
QSettings set(file, QSettings::IniFormat);
set.beginGroup("MainWindow");
restoreState(set.value("State").toByteArray());
set.endGroup();
}
//type: 0-新建佈局 1-恢復佈局 2-儲存佈局 3-佈局另存
void MainWindow::saveLayout(const QString &layout, int type)
{
//如果為空則表示是恢復佈局
if (type == 0) {
App::Layout = layout;
this->changeLayout(App::Layout, true);
return;
} else if (type == 1) {
this->changeLayout(App::Layout, true);
return;
}
QString file = QString("%1/%2.ini").arg(layoutPath).arg(layout);
QSettings set(file, QSettings::IniFormat);
set.beginGroup("MainWindow");
set.setValue("State", saveState());
set.endGroup();
App::Layout = layout;
App::writeConfig();
}
void MainWindow::changeTheme(const QString &theme)
{
//必須是風格改變了才需要重新應用
if (App::Theme != theme) {
App::Theme = theme;
App::writeConfig();
AppInit::Instance()->initTheme();
AppInit::Instance()->initStyle();
}
}
void MainWindow::closeAll()
{
saveLayout(App::Layout, 2);
QUIHelper::sleep(100);