1、前言
佈局方案在整個數據可視化大屏界面電子看板系統中,是除了基礎功能之外的核心功能之一,只有具有了佈局方案這個功能,才能讓用戶隨意調整本身想要的佈局,保存成自定義名稱的佈局配置文件,這樣就大大增長了靈活性,能夠更好的適應各類分辨率,畢竟客戶的電腦運行環境各類各樣的都有,模塊數量衆多,有些不想展現,有些須要特別放大展現,有些須要偶爾全屏展現等,這些佈局用戶均可以本身定義好保存配置方案,存儲到指定的目錄下,下次啓動會自動生成對應的佈局菜單文件讓用戶自行選擇切換。linux
2、電子看板介紹
電子看板是目視化管理的一種表現形式,即對數據的情況一目瞭然地表現,主要是對於管理項目,它經過利用形象直觀而又色彩適宜的各類視覺感知信息來組織現場生產活動,目視管理依據人類的生理特徵,在生產現場充分利用信號燈、標識牌、符號顏色等方式來發出視覺信號,鮮明準確地刺激人的神經末梢,快速地傳遞信息,形象直觀地將潛在的問題和浪費現象都顯現出來。以便任何人均可以及時掌握管理現狀和必要的情報,從而可以快速制定並實施應對措施。所以,管理看板是發現問題、解決問題的很是有效且直觀的手段,是優秀的現場管理必不可少的工具之一。sql
3、功能特色
- 總體總共分三級界面,一級界面是總體佈局,二級界面是單個功能模塊,三級界面是單個控件。
- 子控件包括餅圖+圓環圖+曲線圖+柱狀圖+柱狀分組圖+橫向柱狀圖+橫向柱狀分組圖+合格率控件+百分比控件+進度控件+設備狀態面板+表格數據+地圖控件(包括動態閃爍點+遷徙圖等)+視頻控件+其餘控件等。
- 二級界面能夠自由拖動懸浮,支持最小化最大化關閉,響應雙擊自定義標題欄。
- 數據源支持數據庫採集(默認)、網絡通訊、網絡請求等,可自由設定每一個子界面的採集間隔即數據刷新頻率。
- 採用純QWidget編寫,支持Qt4.6到Qt5.12.3任何版本,支持嵌入式linux好比樹莓派、香橙派、全志、imx6等。
- 提供三個內核版本,自定義控件版本+qchart版本+echart版本。
- 內置多套配色風格樣式,默認紫色,支持任何分辨率。
- 可設置標題+目標分辨率+佈局方案,啓動當即應用。
- 可設置主背景顏色+面板顏色+十字線遊標顏色。
- 可設置多條曲線顏色,沒有設置顏色的狀況下內置15套精美顏色隨機應用。
- 可設置標題欄背景顏色+文字顏色。
- 可設置曲線圖表背景顏色+文字顏色+網格顏色。
- 可設置正常顏色+警惕顏色+報警顏色+禁用顏色+百分比進度顏色。
- 可分別設置各類字體大小,好比全局+軟件名稱+標題欄+子標題欄+加粗標籤等。
- 可設置標題欄高度+表頭高度+行高度。
- 曲線支持遊標+懸停高亮數據點和顯示值,柱狀圖支持頂部(可設置頂端+上部+中間+底部)顯示數據,所有自適應計算位置。
- 主界面直接鼠標右鍵切換佈局+配色方案+關閉開啓某個二級窗體。
- 自動記憶全部子窗口的大小和位置,下次啓動當即應用。
- 動態加載佈局方案菜單,能夠動態新建佈局、恢復佈局、保存佈局、另存佈局等,用戶能夠製造任意佈局。
- 二級窗體,雙擊從主窗體分離出來浮動,能夠自由調整大小。再次雙擊標題欄最大化,再次雙擊還原。
- 每一個模塊均可以自定義採集速度,若是是數據庫採集會自動排隊處理。
- 提供系統設置窗口進行總體的配置參數設置。
4、配置文件說明
(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 |
5、特別說明
- 執行文件同級文件夾有layout+layout_1440+layout_1920,程序默認自動識別分辨率並加載對應的佈局文件夾,好比1920分辨率則從layout_1920文件夾加載佈局,並做爲總體佈局文件夾。
- 程序默認是模擬數據,若是須要從數據庫採集則修改配置文件WorkMode=db便可。
- 若是發現佈局拖動亂了,能夠直接鼠標右鍵選擇恢復佈局便可,在保存佈局之前。
- 在中間地圖模塊鼠標右鍵能夠彈出菜單,切換佈局和配色方案等。
- 在模塊的標題欄上右鍵能夠彈出默認的dock菜單,用來顯示和隱藏各模塊。
- 軟件關閉過程當中會自動保存佈局,下次啓動之後自動應用。
- 若是使用的默認的默認的配色方案好比紫色風格,則配置文件中的顏色所有無效,會自動應用代碼中的顏色,若是須要啓用自定義的顏色,則將配置文件的 Theme=x81eax5b9ax4e49x98cex683c 便可。此時打開軟件會應用配置文件中的顏色。
- 右鍵菜單能夠截圖保存,默認命名爲 配色方案名稱_佈局方案名稱.png 保存在snap目錄下。
- 若是是XP系統請先執行fixff.cmd,用來修復ffmpeg在XP上不可用的BUG。
- 可執行文件下載地址:https://pan.baidu.com/s/1o97I... 提取碼:r2bv ,會不按期更新程序,歡迎各位提出批評和建議。
6、效果圖
7、核心代碼
#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);
exit(0);
}