Qt之log数据展示模块简要实现

Log模块主要用于实时测井数据的显示和测后曲线数据的预览和打印,为更好的展示对Qt中相关知识点的应用,特以Log模块为例对其进行简要实现。内容导图:一、功能需求1、界面效果图Log模块实现曲线

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

 Log模块主要用于实时测井数据的显示和测后曲线数据的预览和打印,为更好的展示对Qt中相关知识点的应用,特以Log模块为例对其进行简要实现。

内容导图:

  Qt之log数据展示模块简要实现

一、功能需求

1、界面效果图

  Qt之log数据展示模块简要实现

  Log模块实现曲线数据的显示及相关属性(曲线颜色、画笔类型、画笔宽度等)的设置

  将上图划分为三个区域:右边为轨道信息显示和管理区,可以参看和设置相关轨道信息

             左上为轨道的Label信息区,轨道中包含的曲线及曲线对应的曲线名、曲线值范围、单位、曲线颜色

             左下为曲线数据显示区

  本实例展示了三个轨道(P、D、R),P轨道有两条曲线(gr、ccl),红色曲线对应的是GR曲线数据,D轨道为深度道,显示曲线数据对应的深度值(每个单元格实际高度为5mm)、R轨道有一条曲线(CCL)

2、功能需求

  (1)打开文件对话框选择log配置文件显示当前配置下的轨道信息(包括上图三个区域)

  (2)加载数据,对相应的曲线进行绘制

  (3)拖动滚动条查看对应深度的曲线数据

  (4)根据右边的区域对轨道和曲线进行管理(信息查看和设置)

二、详细设计

1、UI设计

  从上面的界面效果图可以看到:

  (1)菜单、工具栏、状态栏的实现

  (2)左边为QMidQrea多文档区域,再对单个的文档区域进行QSplitter切割分为TrackView和LogView,trackView用于显示label信息,LogView用于显示曲线数据

  (3)右边为QDockWidget,用于管理track信息,可以实现上下左右4多个区域停靠

2、整体架构设计

  Qt之log数据展示模块简要实现

3. UML类图设计

(1)MainWindow布局

  Qt之log数据展示模块简要实现

(2)类图

  说明:最开始的思想是使用QGraphicsView/QGraphicsScene/QGraphicsitem的视图架构来绘制,最终以失败告终,因为对scene的左上角位置始终无法固定到视口的最上角,窗口变化,滚动条变化会导致scene位置的变化。最终选择在QWidget上进行双缓存绘图。

  Qt之log数据展示模块简要实现

三、Qt相关知识点

1. 菜单、工具栏、状态栏实现

  为了简明扼要直接说步骤(以File菜单下的Open xmlFile子菜单为例):

(1)添加各菜单项的QAction及信号槽

QAction* m_pFileAction;
m_pFileAction = new QAction(QIcon(":/images/openfile.png"), tr("Load Template"), this);
connect(m_pFileAction, SIGNAL(triggered(bool)), this, SLOT(onLloadTemplate()));

(2)添加菜单

QMenu* pFileMenu = menuBar()->addMenu("File");
pFileMenu->addAction(m_pFileAction);

(3)添加工具条

QToolBar* pToolbar = addToolBar(tr("file"));
pToolbar->addAction(m_pFileAction);

(4)添加状态栏

m_pFileAction->setStatusTip(tr("open config file"));
statusBar();

2. QTrackWidget、QMdiArea、QSplitter的实现

  该知识点内容可以看之间的博客:Qt容器组件(二)之QWidgetStack、QMdiArea、QDockWidget

  还是贴一下代码,方便后面查看:

    m_pMdiArea = new QMdiArea(this);
    m_pMdiArea->setBackground(QBrush(Qt::white));
    m_pMdiArea->setViewMode(QMdiArea::TabbedView);
    m_pMdiArea->setTabsClosable(true);
    m_pMdiArea->setTabsMovable(true);
    m_pMdiArea->setTabShape(QTabWidget::TabShape::Triangular);
    m_pMdiArea->setTabPosition(QTabWidget::TabPosition::North);

    setCentralWidget(m_pMdiArea);

  // 停靠窗口
    if (m_pTrackWidget == NULL)
    {
       m_pTrackWidget = new QTrackWidget(this);
       QDockWidget* pDockWidget = new QDockWidget("TrackInfos", this);
       pDockWidget->setFeatures(QDockWidget::DockWidgetFeature::AllDockWidgetFeatures);
       pDockWidget->setWidget(m_pTrackWidget);
       addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, pDockWidget);
       m_pTrackWidget->initWidget();
    }

    QSplitter *pSplitter = new QSplitter(Qt::Vertical, this);
    // track视图窗口
    m_pScrollArea = new ScrollArea;

    m_pTrackView = new TrackView(pSplitter);

    m_pScrollArea->setBackgroundRole(QPalette::Light);     // 添加滚动条
    m_pScrollArea->setWidget(m_pTrackView);
    m_pScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    // logview视图窗口
    m_pLogView = new LogView(pSplitter);

    m_pLogScrollArea = new ScrollArea;
    m_pLogScrollArea->setBackgroundRole(QPalette::Light);     // 添加滚动条
    m_pLogScrollArea->setWidget(m_pLogView);
    m_pLogScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    m_pLogScrollArea->setStep(m_pLogView->getRatioY());

    pSplitter->setWindowTitle(fielInfo.fileName());
    pSplitter->addWidget(m_pScrollArea);
    pSplitter->addWidget(m_pLogScrollArea);
    pSplitter->setStretchFactor(0, 1);  // 设置分隔比例
    pSplitter->setStretchFactor(1, 4);
    m_pMdiArea->addSubWindow(pSplitter);

3. XML文件解析

  Qt提供了DOM、SAX和流方法三种方式进行XML文件解析,这里主要介绍三种解析方法的特点和Dom解析的使用方法,若想了解更多,可以查看官方文档。

(1)DOM解析XML文档的特点

  基于DOM的解析器的核心是在内存中建立和XML文档相对应的树状结构。XML文件的标记、标记中的文本数据和实体等都是内存中的树状结构的某个节点相对应。

     优点:可以方便地操作内存中的树状节点

     缺点:如果XML文件较大,或者只需要解析XML文档的一部分数据,就会占用大量的内存空间

(2)SAX解析XML文档的特点

  SAX解析的核心是事件处理机制,SAX采用事件机制的方式来解析XML文档。使用SAX解析器对XML文档进行解析时,SAX解析器根本不创建任何对象,只是在遇到XML文档的各种标签如文档开始、元素开始、文本、元素结束时触发对应的事件,并将XML元素的内容封装成事件传出去。而程序员则负责提供事件监听器来监听这些事件,从而触发相应的事件处理方法,并通过这些事件处理方法实现对XML文档的访问。

  优点:具有占用内存少,效率高等特点。

  缺点:不便于随机访问任意节点。

(3)流方式解析XML文档的特点 

  QXmlStreamReader使用了递增式的解析器,适合于在整个XML文档中查找给定的标签、读入无法放入内存的大文件以及处理XML的自定义数据。

     优点:快速、方便,分块读取XML文件,可读取大文件

     缺点:递增式解析器,只能顺序遍历XML文件的元素,不能随机访问

     QXmlStreamWriter类提供了简单流接口的XML写入器,写入XML文档只需要调用相应的记号写入函数来写入相关数据。

     优点:快速、方便

     缺点:只能按顺序写入元素,不能删除、修改

(4)DOM方式解析XML文件

   http://blog.51cto.com/9291927/1879135

4. Q_PROPERTY

  关于Q_PROPERTY的相关知识,可以查看直接的博客:Qt属性系统(Qt Property System)

5. QVariant自定义数据类型使用

(1)定义自定义类型,如class QTrack

  需要注意:需实现拷贝构造函数和赋值构造函数

    explicit QTrack(QObject *parent=Q_NULLPTR);
    QTrack(const QTrack& ther) {*this = ther;}
    QTrack& operator=(const QTrack& ther);

(2)Q_DECLARE_METATYPE(QTrack)声明

(3)自定义类型的使用

m_pTrackComb->addItem(pTrack->getName(), QVariant::fromValue(pTrack));

// 得到track对象
QVariant variant = m_pTrackComb->currentData(Qt::UserRole);
QTrack* pTrack = variant.value<QTrack*>();

6. QFileDialog使用

static QString getOpenFileName(QWidget *parent = Q_NULLPTR,
                                   const QString &caption = QString(),
                                   const QString &dir = QString(),
                                   const QString &filter = QString(),
                                   QString *selectedFilter = Q_NULLPTR,
                                   Options options = Options());
QString strFilePath = QFileDialog::getOpenFileName(this, "open xmlFile", "E:/qtquick/Log/config", "file(*.xml)");
    QFileInfo fielInfo(strFilePath);

7. QColorDialog使用

static QColor getColor(const QColor &initial = Qt::white,
                           QWidget *parent = Q_NULLPTR,
                           const QString &title = QString(),
                           ColorDialogOptions options = ColorDialogOptions());
QColor color = QColorDialog::getColor(Qt::blue, this, tr("颜色选择对话框"));

8. QScrollArea

  QScrollArea提供了一个滚动视图到另一个部件。

  滚动区域用于显示一个画面中的子部件的内容。如果部件超过画面的大小,视图可以提供滚动条,这样就都可以看到部件的整个区域。

  为QWidget添加滚动条:

(1)继承QScrollArea

class ScrollArea : public QScrollArea
{
    Q_OBJECT

public:
    explicit ScrollArea(QWidget *parent = Q_NULLPTR);
    ~ ScrollArea();

    void scrollContentsBy(int dx, int dy) Q_DECL_OVERRIDE;
};
#include "scrollarea.h"
#include "logview.h"

#include <QScrollBar>

ScrollArea::ScrollArea(QWidget *parent)
    :QScrollArea(parent)
{

}

ScrollArea::~ScrollArea()
{

}

void ScrollArea::scrollContentsBy(int dx, int dy)
{
    m_pLogView->setDepthOffset(dy);
    return QScrollArea::scrollContentsBy(dx,dy);
}

void ScrollArea::setStep(qreal fRatioY)
{
    QScrollBar *pScrollBar = verticalScrollBar();
    pScrollBar->setSingleStep(fRatioY * 25);   // 25mm
    pScrollBar->setPageStep(fRatioY * 50);    // 50mm
}

(2)为QWidget添加滚动条

m_pLogScrollArea = new ScrollArea;
m_pLogScrollArea->setBackgroundRole(QPalette::Light);     // 添加滚动条
m_pLogScrollArea->setWidget(m_pLogView);
m_pLogScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

9. LogView的实现

  曲线数据的绘制关键:

(1)双缓存绘图

  先在QPixmap上绘图,然后将位图贴到窗口设备上

 void LogView::paintEvent(QPaintEvent *event)
m_pPix = new QPixmap(size());    
QPainter *pMemDc = new QPainter;
m_pPix->fill(Qt::white);
pMemDc->begin(m_pPix);
QPointF point(10, 0);
pMemDc->drawLine()..........
pMemDc->end();

QPainter painter(this);
QPointF pixPoint(0, 0);
painter.drawPixmap(pixPoint, *m_pPix);

  当窗口大小改变时重绘位图m_pPix

 void LogView::resizeEvent(QResizeEvent *event)
void LogView::resizeEvent(QResizeEvent *event)
{
    if (height() > m_pPix->height() || width() > m_pPix->width())
    {
        QPixmap *pNewPix = new QPixmap(size());
        pNewPix->fill(Qt::white);

        QPainter p;
        p.begin(pNewPix);
        p.drawPixmap(QPoint(0,0), *m_pPix);
        p.end();

        m_pPix = pNewPix;
    }

    QWidget::resizeEvent(event);
}

(2)得到设备DPI

QPaintDevice* pDevice = painter.device();
int nDpix = pDevice->logicalDpiX();  // 每英寸多少个点
int nDpiy = pDevice->logicalDpiY();

  若绘图以mm为单位,需得到mm与像素比例:

m_fXRatio = nDpix / 25.4;  // 1mm多少个像素点
m_fYRatio = nDpiy / 25.4;

(3)根据滚动区域和窗口变化设置最大深度和最小深度

void LogView::setDepthOffset(int fOffset)
{
    m_fDepthOffset = fOffset / m_fYRatio / m_nCellMM ;

    m_fMinDepth -= m_fDepthOffset;
    if (m_fMinDepth < 0) m_fMinDepth = 0;
    m_fMaxDepth = m_fMinDepth + m_pPix->size().height() / m_fYRatio / m_nCellMM;

    update();   // 更新视图,重绘
}

10. C++11实现计时器

#ifndef TIMER_H
#define TIMER_H

#include <chrono>
using namespace std;
using namespace chrono;

class Timer
{
public:
    Timer() : m_begin(high_resolution_clock::now()){}
    void reset() {m_begin = high_resolution_clock::now();}

    template<typename Duration=milliseconds>
    int64_t elapsed() const
    {
        return duration_cast<Duration>(high_resolution_clock::now() - m_begin).count();
    }

    // 微妙
    int64_t elapsed_micro() const
    {
        return elapsed<microseconds>();
    }

    //
    int64_t elapsed_seconds() const
    {
        return elapsed<seconds>();
    }

    // 纳秒
    int64_t elapsed_nano() const
    {
        return elapsed<nanoseconds>();
    }
private:
    time_point<high_resolution_clock> m_begin;
};

#endif // TIMER_H

  计时器使用:

Timer t;
qDebug() << t.elapsed();

源码下载:

  https://pan.baidu.com/s/1dezpcpQDGYxW9Z_4BH3avw

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/120081.html原文链接:https://javaforall.net

(0)
上一篇 2021年12月29日 下午1:00
下一篇 2021年12月29日 下午1:00


相关推荐

  • Gliffy confluence插件的激活成功教程

    Gliffy confluence插件的激活成功教程转自 https haoluobo com 2011 09 gliffy confluence creak 参考 gliffyconflu 插件 gliffy confluence plugin 5 1 ja 激活成功教程 Gliffy 是一个在线画流程图的工具 或者简单的说 Gliffy 就是 web 版的 Visio Gliffy 的用户体验非常的好 加打开浏览器就可以使用 使用起来非常的方便

    2026年3月16日
    3
  • 2017.8.22

    2017.8.22

    2022年3月6日
    44
  • 海思Hi3798MV300_Hi3798MV300H_Datasheet-系统[通俗易懂]

    海思Hi3798MV300_Hi3798MV300H_Datasheet-系统[通俗易懂]Hi3798MV300/Hi3798MV300H处理器子系统Hi3798MV300/Hi3798MV300H采用ARMCortex-A53MPCore四核处理器,Cortex-A53MPCore具有以下特点:处理器集成了256KBL2cache。支持ARMv8-A架构。支持DVFS自动调频调压和AVS自适应调压。安全子系统具有以下特性:…

    2022年6月29日
    202
  • linux用yum安装jdk或yum组安装jdk只有java命令没有javac命令

    linux用yum安装jdk或yum组安装jdk只有java命令没有javac命令解决linux上使用yum安装jdk导致有java命令没有javac命令的问题

    2022年5月27日
    43
  • 【js】理解普通函数和箭头函数的区别点

    【js】理解普通函数和箭头函数的区别点普通函数在 es5 中就有了 箭头函数是 es6 中出现的函数形式 当然也可以继续用普通函数 普通函数大家知道 形式基本一致来看看箭头函数 开发时根据实际情况可以省略一些东西单条处理可以省略 return 和 大括号 单个参数可以省略 小括号 箭头函数不能作为构造函数不能 new 会报错箭头函数不绑定 arguments 但是可使用 rest 参数这是普通函数 arguments 可以使用

    2025年7月14日
    2
  • vite.config.js常用配置

    vite.config.js常用配置vite 是一个基于 Vue3 单文件组件的非打包开发服务器 它做到了本地快速开发启动 快速的冷启动 不需要等待打包操作 即时的热模块更新 替换性能和模块数量的解耦让更新飞起 真正的按需编译 不再等待整个应用编译完成 这是一个巨大的改变 vite 项目中使用的不是 vue config js 而是 vite config jsvite config js 配置 constpath require path vite config js orvite config ts

    2026年3月19日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号