Qt4利用MVC开发曲线数据编辑器

目录

1 需求

2 开发流程

1 搭建框架

2 构造函数

3 打开工程

4 实现应用程序参数加载

5 QCustomPlot和TableView的联动

6 数据的可视化修改

7 列表点击事件事先键盘控制

8 表格实现复制,粘贴,删除等一系列功能

9 曲线实现自适应范围和统一范围


1 需求

之前编过1个曲线编辑器,但有几个问题,1是加载太慢,2是没法保存工程。

现在将需求重新整理一下,再开发个曲线编辑器。此外也总结了三点技术问题,分别为:

(1)曲线空间QCustomPlot和表格控件TableView的联动,目的是实现曲线编辑;

(2)数据分类显示,目的是数据按不同分类来绘图,避免叠合在一起看不清。

(3)表格实现复制,粘贴,删除等一系列功能。

(4)列表控件实现键盘控制,解放鼠标,加速曲线切换。

(5)不同曲线实现自适应范围和统一范围。用于对比。

2 开发流程

1 搭建框架

新建main window工程,并使其支持中文

#include <QtGui/QApplication>
#include "mainwindow.h"
#include <QTextCodec>int main(int argc, char *argv[])
{QApplication a(argc, argv);// 文本编码规定QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF8"));QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF8"));QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF8"));MainWindow w;w.show();return a.exec();
}

2 构造函数

在构造函数中定好模型,视图以及控件初始化。

MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);winName = "磁测剖面编辑器 V1.0";setWindowTitle(winName);// 默认编辑开关为否customEditOn = false;// 默认曲线范围为自适应curveXfit = true;curveYfit = true;// 表格模型视图model = new QStandardItemModel(0,22);model->setHorizontalHeaderLabels(QStringList()<<"Line"<<"Point"<<"PLon"<<"PLat"<<"DiuCorr"<<"Read"<<"Sq"<<"Lon"<<"Lat"<<"Elevation"<<"Date"<<"Time"<<"Instr"<<"GeoMag"<<"ΔT"<<"Note"<<"PX"<<"PY"<<"X"<<"Y"<<"Pdistance"<<"Anomaly");connect(model,SIGNAL(dataChanged(QModelIndex,QModelIndex)),this,SLOT(slotDataChanged(QModelIndex,QModelIndex)));proxyModel = new QSortFilterProxyModel(this);proxyModel->setSourceModel(model);proxyModel->setFilterKeyColumn(0);ui->tableView->setModel(proxyModel);ui->tableView->resizeColumnsToContents();ui->tableView->resizeRowsToContents();// 列表模型视图lines = new QStringListModel;ui->listView->setModel(lines);ui->listView->setEditTriggers(false);// plot点击事件connect(ui->curveView, SIGNAL(plottableClick(QCPAbstractPlottable*,int,QMouseEvent*)),this, SLOT(graphClicked(QCPAbstractPlottable*,int)));// 进度条ui->progressBar->setRange(0,100);ui->progressBar->setValue(0);ui->progressBar->hide();}

3 打开工程

这里要重点改一下,先来看之前的逻辑

  • 指定文件名
  • 读取文件到局部变量data
  • 计算公里网
  • 多级排序
  • 计算测线名称及点数
  • 计算距离和异常列
  • 写入模型
  • 挂载测线列表并触发点击
  • 控件显示。

这里要重点改新增加的这几列,有些列需要预留。

更改原始文件,将投影变换的功能移除,现在的逻辑变为:

  • 读取数据
  • 点名分离为线和点
  • 计算测线
  • 计算点数(这个实际MVC中不用,只是用于后期用户统计)
  • 数据写入模型
  • 挂载测线列表
  • 触发点击事件。

此逻辑较老版的更为简介,且加载速度更快。

4 实现应用程序参数加载

为在主程序中调用曲线编辑器,可采用QProcess来调用,这时候需要改造构造函数,并重写打开action。具体代码如下:

// 打开line文件
void MainWindow::on_actionOpen_triggered()
{// 1指定文件QString tmpName = QFileDialog::getOpenFileName(this,"Open","","*.txt");if(tmpName.isEmpty())return;fileName = tmpName;open(QStringList()<<fileName);}

所有的第3步提到的业务逻辑全部打包至open函数中,这样就可以实现在构造函数中调用,代码如下:

 if(!inNameList.isEmpty())open(inNameList);

这里要注意的是,传入参数是个list,需要取第0个值作为文件名。

5 QCustomPlot和TableView的联动

由于QCustomPlot仅仅为绘图库,不是MVC结构,因此只能实现2个单向的联动,以此来模拟MVC机制。

主要实现2个流程:

(1)当绘图数据点击时,实现表格的选择;

(2)当表格数据修改时,实现绘图的更新。

具体第1个方法的代码是:

void MainWindow::graphClicked(QCPAbstractPlottable *plottable, int dataIndex)
{// plot中只有QCPGraphs,因此可以立即调用interface1D()// 建议先检查interface1D()是否返回非零double dataValue = plottable->interface1D()->dataMainValue(dataIndex);QString message = QString("Clicked on graph '%1' at data point #%2 with value %3.").arg(plottable->name()).arg(dataIndex).arg(dataValue);ui->statusBar->showMessage(message);// 少数据的话就不准了ui->tableView->setCurrentIndex(proxyModel->index(dataIndex,19,QModelIndex()));
}

第2个方法的实现代码为:

/* 数据改动回调 */
void MainWindow::slotDataChanged(QModelIndex ind1, QModelIndex ind2)
{Q_UNUSED(ind1)Q_UNUSED(ind2)if(customEditOn == true)plot();
}

代码解析:1个是绘图库控件单击事件,并不修改表格数据;1个是表格数据的修改事件槽函数,调用重新绘图方法。因此两个并不是咬合的关系,但却恰恰实现了需求的功能。

6 数据的可视化修改

下面来展示下第5点所属的修改过程。

文件加载了数十条曲线,上图显示的第13条。这条曲线明显被一些废点所影响了,如果单纯看表格是很难找到的,毕竟有上千个数据。而曲线则可以轻易的找到废点位置。

只需要点击曲线,就能找到废点在表格中的位置。

选中之后,用户可以选择删除或者修改。

可见删除之后,绘图库控件即时进行了刷新。曲线恢复了正常形态。其余的废点也可以按照这种放方法进行处理。

7 列表点击事件事先键盘控制

在切换曲线时,需要鼠标逐个选择列表项,这相当的麻烦,因此需要实现键盘事件,以此来加速曲线的切换操作。

具体的代码为:


// 方向键上下加回车可调用点击事件
void MainWindow::on_listView_activated(const QModelIndex &index)
{on_listView_clicked(index);
}

代码较为简单,仅仅是调用了点击事件。

// 测线列表 单击事件
void MainWindow::on_listView_clicked(const QModelIndex &index)
{// 模型过滤后绘图int row = index.row();QString ln = lines->stringList().at(row);// 关闭编辑标记后再修改代理模型 避免在修改时频繁调用plotcustomEditOn = false;proxyModel->setFilterRegExp(QRegExp(ln, Qt::CaseInsensitive, QRegExp::FixedString));plot();customEditOn = true;
}

具体逻辑为:先获取选中行的序号,再找到字符串,之后设置proxymodel的正则化过滤器,刷新绘图后,打开修改开关。

通过上述2个函数配合,就可以实现回车与点击事件的同步操作。简化了曲线切换的麻烦。

8 表格实现复制,粘贴,删除等一系列功能

表格数据需要实现复制,粘贴,删除等一系列功能,这就涉及到tableview的子类化问题,上一篇博文我们用mainwindow来实现复制粘贴,本节则采用对qtableview子类化的方式,重写event函数来达到此目的。

下面是代码:


/*  实现多选的复制粘贴 */
void TableView::keyPressEvent(QKeyEvent *keyEvent)
{if(keyEvent->matches(QKeySequence::Copy))//复制{QModelIndexList indexList = selectionModel()->selectedIndexes();if(indexList.isEmpty())return;int startRow = indexList.first().row();int endRow = indexList.last().row();int startCol = indexList.first().column();int endCol = indexList.last().column();QStringList clipboardTextList;for(int i = startRow;i <= endRow;i++){QStringList rowText;for(int j = startCol;j <= endCol;j++){rowText.append(model()->data(model()->index(i,j)).toString());}clipboardTextList.append(rowText.join("\t"));}QString clipboardText = clipboardTextList.join("\n" );QApplication::clipboard()->setText(clipboardText);}else if (keyEvent->matches(QKeySequence::Paste)){QString clipboardText = QApplication::clipboard()->text();if(clipboardText.isEmpty())return;QStringList rowTextList = clipboardText.split('\n');if(rowTextList.last().isEmpty())//从word或者excel复制的内容后面可能会带'\n',导致split出来后面有个空字符串。rowTextList.removeLast();QModelIndexList indexList = selectionModel()->selectedIndexes();if(indexList.isEmpty())return;QModelIndex startIndex = indexList.first();for(int i = 0;i < rowTextList.size();i++){QStringList itemTextList = rowTextList.at(i).split('\t');for(int j = 0;j < itemTextList.size();j++){QModelIndex curIndex = model()->index(i + startIndex.row(),j + startIndex.column());if(curIndex.isValid()){model()->setData(curIndex,itemTextList.at(j));}}}}else if (keyEvent->matches(QKeySequence::Delete)){// 获取选中行QItemSelectionModel *selections = selectionModel();QModelIndexList selected = selections->selectedIndexes();// 循环选中的各个index并写为空foreach(QModelIndex index,selected){model()->setData(index,"");}}else if (keyEvent->matches(QKeySequence::SelectAll)){QModelIndex topLeft;QModelIndex bottomRight;topLeft = model()->index(0,0);bottomRight = model()->index(model()->rowCount()-1,model()->columnCount()-1);QItemSelection selection(topLeft,bottomRight);selectionModel()->select(selection,QItemSelectionModel::Select);}else if (keyEvent->matches(QKeySequence::MoveToNextLine)){if(currentIndex().row()>-1){if(currentIndex().row()<model()->rowCount()-1)setCurrentIndex(model()->index(currentIndex().row()+1,currentIndex().column()));}}else if (keyEvent->matches(QKeySequence::MoveToPreviousLine)){if(currentIndex().row()>0){setCurrentIndex(model()->index(currentIndex().row()-1,currentIndex().column()));}}else if (keyEvent->matches(QKeySequence::MoveToNextChar)){if(currentIndex().column()>-1){if(currentIndex().column()<model()->columnCount()-1)setCurrentIndex(model()->index(currentIndex().row(),currentIndex().column()+1));}}else if (keyEvent->matches(QKeySequence::MoveToPreviousChar)){if(currentIndex().column()>0){setCurrentIndex(model()->index(currentIndex().row(),currentIndex().column()-1));}}else if (keyEvent->matches(QKeySequence::MoveToNextPage)){int row = currentIndex().row();if(row>-1){int col = currentIndex().column();int step = 20;int count = model()->rowCount();if(row+step<count-1)setCurrentIndex(model()->index(row+step,col));elsesetCurrentIndex(model()->index(count-1,col));}}else if (keyEvent->matches(QKeySequence::MoveToPreviousPage)){int row = currentIndex().row();if(row>-1){int col = currentIndex().column();int step = 20;if(row-step>0)setCurrentIndex(model()->index(row-step,col));elsesetCurrentIndex(model()->index(0,col));}}
}

代码外层是比较简单的判断语句,分别实现了ctrl+c,ctrl+v,ctrl+a,delete等功能。这样就可以对表格实现较多的单选,多选,指定区域的复制,粘贴,删除等操作。

9 曲线实现自适应范围和统一范围

主要用lineEdit控件和绘图库控件配合完成。

代码如下:


// 横坐标范围模式切换
void MainWindow::on_actionXlim_toggled(bool arg1)
{if(arg1==true)curveXfit = false;elsecurveXfit = true;
}// 纵坐标范围模式切换
void MainWindow::on_actionYlim_toggled(bool arg1)
{if(arg1==true)curveYfit = false;elsecurveYfit = true;
}

通过切换自适应开关来实现绘图范围的控制。在绘图plot中实现:

   // 设置绘图范围QString xmin = ui->xmin->text();QString xmax = ui->xmax->text();if(curveXfit==true || xmin=="" || xmax=="")ui->curveView->xAxis->rescale();elseui->curveView->xAxis->setRange(xmin.toDouble(),xmax.toDouble());QString ymin = ui->ymin->text();QString ymax = ui->ymax->text();if(curveYfit==true || ymin=="" || ymax=="")ui->curveView->yAxis->rescale();elseui->curveView->yAxis->setRange(ymin.toDouble(),ymax.toDouble());

用一个判断来实现绘图范围控制的切换,以此来实现曲线的对比和显示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/174243.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JMeter】运行方式

第一种&#xff1a; 使用GUI 操作&#xff1a; 在JMeter界面菜单导航上点击运行按钮 一般用作创建TestPlan和调试脚本增加java堆空间来满足测试环境 第二种&#xff1a;使用CLI(Command Line) 性能测试一般请求量比较大&#xff0c;为了节省资源 CLI参数用法&#xff1a; 字段…

Flask Echarts 实现历史图形查询

Flask前后端数据动态交互涉及用户界面与服务器之间的灵活数据传递。用户界面使用ECharts图形库实时渲染数据。它提供了丰富多彩、交互性强的图表和地图&#xff0c;能够在网页上直观、生动地展示数据。ECharts支持各种常见的图表类型&#xff0c;包括折线图、柱状图、饼图、散点…

[Spring] 字节一面~Spring 如何解决循环依赖问题 以及 @resource 与 @autowire 同时存在时谁生效

文章目录 Spring 如何解决循环依赖问题resource 与 autowire 同时存在时谁生效 Spring 如何解决循环依赖问题 Spring在实例化一个bean的时候&#xff0c;是首先递归实例化其所依赖的所有bean&#xff0c;直到某个bean没有依赖其他bean&#xff0c;此时就会将该实例返回&#x…

【JavaWeb】Servlet

Servlet 文章目录 Servlet一、简介二、开发流程三、生命周期四、ServletConfig和ServletContext五、HttpServletRequest常见API六、HttpServletResponse常见API七、请求转发和响应重定向7.1 概述7.2 请求转发7.3 响应重定向 八、请求与响应乱码问题8.1 GET与POST请求乱码8.2 响…

内网穿透的应用-Jupyter Notbook+cpolar内网穿透实现公共互联网访问使用数据分析工作

文章目录 1.前言2.Jupyter Notebook的安装2.1 Jupyter Notebook下载安装2.2 Jupyter Notebook的配置2.3 Cpolar下载安装 3.Cpolar端口设置3.1 Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 在数据分析工作中&#xff0c;使用最多的无疑就是各种函数、图表、…

五、Lua流程控制与函数

一、流程控制 &#xff08;一&#xff09;含义 Lua 编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码&#xff0c;在条件为 false 时执行其他指定代码。 &#xff08;二&#xff09;原型 if (成立) then执行体1else执行体2 end…

字符串入门算法题!

概述 字符串和数组一样算是比较简单的题目&#xff0c;正适合打算法基础&#xff0c;一定要认真对待&#xff01;&#xff01;&#xff01; 字符串类型的算法问题可以分为简单、中等和困难的难度级别&#xff0c;基础类型一些基本的字符串处理问题&#xff0c;如字符串的拼接…

自动化部署 扩容openGauss —— Ansible for openGauss

前言 大家好&#xff0c;今天我们为大家推荐一套基于Ansible开发的&#xff0c;自动化部署及扩容openGauss的脚本工具&#xff1a;Ansible for openGauss&#xff08;以下简称 AFO&#xff09;。 通过AFO&#xff0c;我们只需简单修改一些配置文件&#xff0c;即可快速部署多种…

数智赋能 锦江汽车携手苏州金龙打造高质量盛会服务

作为一家老牌客运公司&#xff0c;成立于1956年的上海锦江汽车服务有限公司&#xff08;以下简称锦江汽车&#xff09;&#xff0c;拥有1200多辆大巴和5000多辆轿车&#xff0c;是上海乃至长三角地区规模最大的专业旅游客运公司。面对客运市场的持续萎缩&#xff0c;锦江汽车坚…

王道数据结构课后代码题p19 第14题请设计一个尽可能高效的算法,计算并输出所有可能的三元组(a,b,c) 中的最小距离。(c语言代码实现)

本题其实就是找a到c的最小值 有讲解p19 第14题 c语言实现王道数据结构课后代码题_哔哩哔哩_bilibili 下方有图&#xff1a; 本题代码如下 int abs(int a)//计算绝对值 {if (a < 0)return -a;elsereturn a; } int min(int a, int b, int c)//a是否为三个数中的最小值 {if …

基于xml配置的AOP

目录 xml方式AOP快速入门 xml方式AOP配置详解 xml方式AOP快速入门 xml方式配置AOP的步骤 导入AOP相关坐标 <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version></de…

二叉树题目:结点与其祖先之间的最大差值

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;结点与其祖先之间的最大差值 出处&#xff1a;1026. 结点与其祖先之间的最大差值 难度 5 级 题目描述 要求 给…

无人售货奶柜:方便快捷,新鲜畅享

无人售货奶柜&#xff1a;方便快捷&#xff0c;新鲜畅享 无人售货奶柜&#xff0c;便捷、多样、实时监控。随时选择您喜爱的奶制品和饮料&#xff0c;快速支付&#xff0c;满足个性口味。24小时全天候运营&#xff0c;无时间限制&#xff0c;随时满足您的购物需求。借助先进的技…

在Rust中编写自动化测试

1.摘要 Rust中的测试函数是用来验证非测试代码是否是按照期望的方式运行的, 测试函数体通常需要执行三种操作:1.设置任何所需的数据或状态;2.运行需要测试的代码;3.断言其结果是我们所期望的。本篇文章主要探讨了Rust自动化测试的几种常见场景。 2.测试函数详解 在Rust项目工…

使用Python编写数独游戏Sudoku教程

数独是各种应用程序中流行的益智类拼图游戏。数独板是一个99的网格&#xff0c;玩家必须在每行、每列和33的子网格中放置一次数字1到9&#xff0c;并且只能放置一次。游戏开始时&#xff0c;有几个空格已经用数字填充&#xff0c;称为givens。一个好的数独谜题应该只有一个可能…

Linux MeterSphere一站式开源持续测试平台远程访问

文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…

计算机毕业设计 基于SpringBoot的智能停车场计费系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

linux下的工具---yum

一、什么是yum yum是Linux下的软件包管理器 二、什么是软件包管理器 1、在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. 2、但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在…

IDEA中Tomcat启动web项目

1.首先【Run】-->【Edit Configurations】&#xff0c;进入对应功能界面 2.点击左上角【】&#xff0c;选择Tomcat Server -->Local 3.Name输入自己中意的&#xff0c;下面两个port&#xff0c;保证没被占用就行 4.切到【Deployment】页签&#xff0c;点击【】&#xff…

(十三)Flask之特殊装饰器详解

目录&#xff1a; Flask中用作装饰器的特殊的函数第一部分&#xff1a;before_request和after_request一、 before_request装饰器&#xff1a;二、after_request装饰器&#xff1a;三、多个before_request和after_request执行流程分析&#xff1a; 首先—理论讲解&#xff1a;然…