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. 每个模块都可以自定义采集速度,如果是数据库采集会自动排队处理。
  22. 提供系统设置窗口进行整体的配置参数设置。

五、特别说明

  1. 执行文件同级文件夹有layout+layout_1440+layout_1920,程序默认自动识别分辨率并加载对应的布局文件夹,比如1920分辨率则从layout_1920文件夹加载布局,并作为整体布局文件夹。
  2. 程序默认是模拟数据,如果需要从数据库采集则修改配置文件WorkMode=db即可。
  3. 如果发现布局拖动乱了,可以直接鼠标右键选择恢复布局即可,在保存布局以前。
  4. 在中间地图模块鼠标右键可以弹出菜单,切换布局和配色方案等。
  5. 在模块的标题栏上右键可以弹出默认的dock菜单,用来显示和隐藏各模块。
  6. 软件关闭过程中会自动保存布局,下次启动以后自动应用。
  7. 如果使用的默认的默认的配色方案比如紫色风格,则配置文件中的颜色全部无效,会自动应用代码中的颜色,如果需要启用自定义的颜色,则将配置文件的 Theme=\x81ea\x5b9a\x4e49\x98ce\x683c 即可。此时打开软件会应用配置文件中的颜色。
  8. 右键菜单可以截图保存,默认命名为 配色方案名称_布局方案名称.png 保存在snap目录下。
  9. 如果是XP系统请先执行fixff.cmd,用来修复ffmpeg在XP上不可用的BUG。
  10. 可执行文件下载地址: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);

    exit(0);

}

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏AiSmart4J

Spring Boot 快速入门系列(V)—— 事务管理篇之 @Transactional

《Spring Boot 快速入门系列》数据操作篇之 Spring Data JPA、JdbcTemplate 和 MyBatis 已经结束,小伙伴们是否了解和...

18230
来自专栏数据和云

【硬货】Oracle数据库出现问题时,这十个脚本帮你快速定位原因

墨墨导读:本文讲述各种场景下的通用处理思路,分享用到的一些脚本,帮助大家快速定位问题并解决,减少业务的中断事件。

15430
来自专栏编程坑太多

『互联网架构』调⽤链系统概述(107)

PS:这次说了互联网架构调用链系统的概述,这个工具存在的意义,以及有哪些类似的成熟工具,下次咱们一起说说他们的底层实现。

13550
来自专栏Linyb极客之路

运维监控之使用Prometheus + Grafana 监控 mysql

上篇我们介绍如何通过Prometheus + Grafana +node explorer来监控linux,本文介绍一下如何通过Prometheus + Gra...

15030
来自专栏wordpress建站吧

原来搭建一个属于自己的网站如此简单?半个小时可搞定

拥有自己的独立网站,做站长,很多人认为很难,既不懂代码也不懂技术没有什么基础。其实不难,代码可以完全不懂,没有建站基础也能很轻松的拥有一个属于自己的网站。

24530
来自专栏AiSmart4J

Spring Boot 快速入门系列(IV)—— 数据操作篇之 MyBatis

从《Spring Boot 快速入门系列》数据操作篇前两篇中(Spring Boot 快速入门系列(II)—— 数据操作篇之 Spring Data JPA,S...

11540
来自专栏Python无止境

小型的编程项目有哪些值得推荐?这本神书写了 22 个,个个了不得

在开始正题之前,先介绍一下它所属的系列。该系列叫 AOSA,是“The Architecture of Open Source Applications”的简称...

17430
来自专栏Hadoop实操

0671-6.2.0-如何将CDH5.12的Hive元数据迁移到CDH6.2

这里我们假定一个场景,你需要迁移CDH5.12到CDH6.2,CDH5.12和CDH6.2分别是两个不同的集群,我们的工作主要是HDFS数据和各种元数据从CDH...

28430
来自专栏AustinDatabases

What's up MYSQL 8 在性能设计上的改变(redo log)

MYSQL 8 的在性能上的设计在MYSQL 的历史上是具有突破性的,也是从底层的架构上进行的改变,可能未来MYSQL 越来越不像原来的MYSQL,长远看MYS...

10420
来自专栏wordpress建站吧

WordPress建站:修改数据库密码导致WP数据库连接错误解决办法?

修改了数据库密码导致wordpress数据库连接错误的解决办法?越来越多的站长使用wordpress作为个人站的首选,但是很多新手司机可能会遇到一些或大或小的问...

15440

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励