Log模塊主要用於實時測井數據的顯示和測後曲線數據的預覽和打印,爲更好的展現對Qt中相關知識點的應用,特以Log模塊爲例對其進行簡要實現。html
內容導圖:
1、功能需求
一、界面效果圖
Log模塊實現曲線數據的顯示及相關屬性(曲線顏色、畫筆類型、畫筆寬度等)的設置程序員
將上圖劃分爲三個區域:右邊爲軌道信息顯示和管理區,能夠參看和設置相關軌道信息緩存
左上爲軌道的Label信息區,軌道中包含的曲線及曲線對應的曲線名、曲線值範圍、單位、曲線顏色架構
左下爲曲線數據顯示區函數
本實例展現了三個軌道(P、D、R),P軌道有兩條曲線(gr、ccl),紅色曲線對應的是GR曲線數據,D軌道爲深度道,顯示曲線數據對應的深度值(每一個單元格實際高度爲5mm)、R軌道有一條曲線(CCL)工具
二、功能需求
(1)打開文件對話框選擇log配置文件顯示當前配置下的軌道信息(包括上圖三個區域)佈局
(2)加載數據,對相應的曲線進行繪製ui
(3)拖動滾動條查看對應深度的曲線數據this
(4)根據右邊的區域對軌道和曲線進行管理(信息查看和設置)spa
2、詳細設計
一、UI設計
從上面的界面效果圖能夠看到:
(1)菜單、工具欄、狀態欄的實現
(2)左邊爲QMidQrea多文檔區域,再對單個的文檔區域進行QSplitter切割分爲TrackView和LogView,trackView用於顯示label信息,LogView用於顯示曲線數據
(3)右邊爲QDockWidget,用於管理track信息,能夠實現上下左右4多個區域停靠
二、總體架構設計
3. UML類圖設計
(1)MainWindow佈局
(2)類圖
說明:最開始的思想是使用QGraphicsView/QGraphicsScene/QGraphicsitem的視圖架構來繪製,最終以失敗了結,由於對scene的左上角位置始終沒法固定到視口的最上角,窗口變化,滾動條變化會致使scene位置的變化。最終選擇在QWidget上進行雙緩存繪圖。
3、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();