Qt编写数据可视化大屏界面电子看板1-布局方案
bigegpt 2024-10-12 05:25 68 浏览
一、前言
布局方案在整个数据可视化大屏界面电子看板系统中,是除了基础功能以外的核心功能之一,只有具备了布局方案这个功能,才能让用户随意调整自己想要的布局,保存成自定义名称的布局配置文件,这样就大大增加了灵活性,可以更好的适应各种分辨率,毕竟客户的电脑运行环境各种各样的都有,模块数量众多,有些不想展示,有些需要特别放大展示,有些需要偶尔全屏展示等,这些布局用户都可以自己定义好保存配置方案,存储到指定的目录下,下次启动会自动生成对应的布局菜单文件让用户自行选择切换。
二、电子看板介绍
电子看板是目视化管理的一种表现形式,即对数据的状况一目了然地表现,主要是对于管理项目,它通过利用形象直观而又色彩适宜的各种视觉感知信息来组织现场生产活动,目视管理依据人类的生理特征,在生产现场充分利用信号灯、标识牌、符号颜色等方式来发出视觉信号,鲜明准确地刺激人的神经末梢,快速地传递信息,形象直观地将潜在的问题和浪费现象都显现出来。以便任何人都可以及时掌握管理现状和必要的情报,从而能够快速制定并实施应对措施。因此,管理看板是发现问题、解决问题的非常有效且直观的手段,是优秀的现场管理必不可少的工具之一。(这段是复制的,哈哈!)
三、功能特点
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)、基本配置参数
(2)、颜色配置参数
(3)、字体和尺寸配置参数
(4)、采集速度配置参数,单位毫秒
(5)、本地数据库配置参数
五、特别说明
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. 会不定期更新程序,欢迎各位提出批评和建议。
六、效果图
七、核心代码
#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);
}
相关推荐
- 最全的MySQL总结,助你向阿里“开炮”(面试题+笔记+思维图)
-
前言作为一名编程人员,对MySQL一定不会陌生,尤其是互联网行业,对MySQL的使用是比较多的。对于求职者来说,MySQL又是面试中一定会问到的重点,很多人拥有大厂梦,却因为MySQL败下阵来。实际上...
- Redis数据库从入门到精通(redis数据库设计)
-
目录一、常见的非关系型数据库NOSQL分类二、了解Redis三、Redis的单节点安装教程四、Redis的常用命令1、Help帮助命令2、SET命令3、过期命令4、查找键命令5、操作键命令6、GET命...
- netcore 急速接入第三方登录,不看后悔
-
新年新气象,趁着新年的喜庆,肝了十来天,终于发了第一版,希望大家喜欢。如果有不喜欢看文字的童鞋,可以直接看下面的地址体验一下:https://oauthlogin.net/前言此次带来得这个小项目是...
- 精选 30 个 C++ 面试题(含解析)(c++面试题和答案汇总)
-
大家好,我是柠檬哥,专注编程知识分享。欢迎关注@程序员柠檬橙,编程路上不迷路,私信发送以下关键字获取编程资源:发送1024打包下载10个G编程资源学习资料发送001获取阿里大神LeetCode...
- Oracle 12c系列(一)|多租户容器数据库
-
作者杨禹航出品沃趣技术Oracle12.1发布至今已有多年,但国内Oracle12C的用户并不多,随着12.2在去年的发布,选择安装Oracle12c的客户量明显增加,在接下来的几年中,Or...
- flutter系列之:UI layout简介(flutter-ui-nice)
-
简介对于一个前端框架来说,除了各个组件之外,最重要的就是将这些组件进行连接的布局了。布局的英文名叫做layout,就是用来描述如何将组件进行摆放的一个约束。在flutter中,基本上所有的对象都是wi...
- Flutter 分页功能表格控件(flutter 列表)
-
老孟导读:前2天有读者问到是否有带分页功能的表格控件,今天分页功能的表格控件详细解析来来。PaginatedDataTablePaginatedDataTable是一个带分页功能的DataTable,...
- Flutter | 使用BottomNavigationBar快速构建底部导航
-
平时我们在使用app时经常会看到底部导航栏,而在flutter中它的实现也较为简单.需要用到的组件:BottomNavigationBar导航栏的主体BottomNavigationBarI...
- Android中的数据库和本地存储在Flutter中是怎样实现的
-
如何使用SharedPreferences?在Android中,你可以使用SharedPreferencesAPI来存储少量的键值对。在Flutter中,使用Shared_Pref...
- Flet,一个Flutter应用的实用Python库!
-
▼Flet:用Python轻松构建跨平台应用!在纷繁复杂的Python框架中,Flet宛如一缕清风,为开发者带来极致的跨平台应用开发体验。它用最简单的Python代码,帮你实现移动端、桌面端...
- flutter系列之:做一个图像滤镜(flutter photo)
-
简介很多时候,我们需要一些特效功能,比如给图片做个滤镜什么的,如果是h5页面,那么我们可以很容易的通过css滤镜来实现这个功能。那么如果在flutter中,如果要实现这样的滤镜功能应该怎么处理呢?一起...
- flutter软件开发笔记20-flutter web开发
-
flutterweb开发优势比较多,采用统一的语言,就能开发不同类型的软件,在web开发中,特别是后台式软件中,相比传统的html5开发,更高效,有点像c++编程的方式,把web设计出来了。一...
- Flutter实战-请求封装(五)之设置抓包Proxy
-
用了两年的flutter,有了一些心得,不虚头巴脑,只求实战有用,以供学习或使用flutter的小伙伴参考,学习尚浅,如有不正确的地方还望各路大神指正,以免误人子弟,在此拜谢~(原创不易,转发请标注来...
- 为什么不在 Flutter 中使用全局变量来管理状态
-
我相信没有人用全局变量来管理Flutter应用程序的状态。毫无疑问,我们的Flutter应用程序需要状态管理包或Flutter的基本小部件(例如InheritedWidget或St...
- Flutter 攻略(Dart基本数据类型,变量 整理 2)
-
代码运行从main方法开始voidmain(){print("hellodart");}变量与常量var声明变量未初始化变量为nullvarc;//未初始化print(c)...
- 一周热门
- 最近发表
-
- 最全的MySQL总结,助你向阿里“开炮”(面试题+笔记+思维图)
- Redis数据库从入门到精通(redis数据库设计)
- netcore 急速接入第三方登录,不看后悔
- 精选 30 个 C++ 面试题(含解析)(c++面试题和答案汇总)
- Oracle 12c系列(一)|多租户容器数据库
- flutter系列之:UI layout简介(flutter-ui-nice)
- Flutter 分页功能表格控件(flutter 列表)
- Flutter | 使用BottomNavigationBar快速构建底部导航
- Android中的数据库和本地存储在Flutter中是怎样实现的
- Flet,一个Flutter应用的实用Python库!
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- libcrypto.so (74)
- linux安装minio (74)
- ubuntuunzip (67)
- vscode使用技巧 (83)
- secure-file-priv (67)
- vue阻止冒泡 (67)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)