Qt之log數據展示模塊簡要實現


 Log模塊主要用於實時測井數據的顯示和測后曲線數據的預覽和打印,為更好的展示對Qt中相關知識點的應用,特以Log模塊為例對其進行簡要實現。

內容導圖:

  

一、功能需求

1、界面效果圖

  

  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、整體架構設計

  

3. UML類圖設計

(1)MainWindow布局

  

(2)類圖

  說明:最開始的思想是使用QGraphicsView/QGraphicsScene/QGraphicsitem的視圖架構來繪制,最終以失敗告終,因為對scene的左上角位置始終無法固定到視口的最上角,窗口變化,滾動條變化會導致scene位置的變化。最終選擇在QWidget上進行雙緩存繪圖。

  

三、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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM