前言
將一個(gè)二進(jìn)制文件直接拖放到Qt Creator中可以直接查看到以十六進(jìn)制顯示的數(shù)據(jù)格式,如:
要實(shí)現(xiàn)一個(gè)這樣的效果,還是要花不少時(shí)間的。
在網(wǎng)上找了挺多示例,其中一個(gè)開源代碼效果不錯(cuò)(參考這里),但是是在QWidget中實(shí)現(xiàn)的,通過(guò)繼承QAbstractScrollArea來(lái)實(shí)現(xiàn)數(shù)據(jù)滾動(dòng)繪制。
如果QML中要自定義在paint中繪制,需要繼承QQuickPaintedItem,那要實(shí)現(xiàn)這樣的滾動(dòng)分頁(yè)展示就比較麻煩了。嘗試了一下直接將Widget中實(shí)現(xiàn)的效果封裝一下放到QML中去使用,就會(huì)比較省事,畢竟原有的開源效果已經(jīng)做得很好了。
以此思路,開搞?。?!
先來(lái)看效果:
這是在QML項(xiàng)目中實(shí)現(xiàn)的,中間的數(shù)據(jù)展示部分是使用QHexView代碼Widget實(shí)現(xiàn)的。
本文demo 點(diǎn)擊下載文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-701534.html
正文
將Widget嵌入到QML中使用,先來(lái)看會(huì)遇到什么問(wèn)題。
上面說(shuō)到,QML中要自定義在paint中繪制,需要繼承QQuickPaintedItem,然后重寫paint函數(shù),而QML Scene Graph場(chǎng)景圖繪制是在兩個(gè)不同的線程中,這個(gè)在Qt幫助文檔中有說(shuō)明:
也就是說(shuō),paint()不是從主GUI線程調(diào)用的,而是從啟用GL的渲染器線程調(diào)用的.。
那這會(huì)帶來(lái)什么問(wèn)題呢,繼續(xù)看
由于主UI框架是用QML做的,我們想把Widget嵌入到QML中使用,就需要新建一個(gè)類繼承QQuickPaintedItem
然后把Widget放到其中,封裝起來(lái)后將該類通過(guò)qmlRegisterType
注冊(cè)交給QML去引用。那Widget窗口中的各種事件就需要從QML這邊發(fā)送過(guò)去,所以需要進(jìn)行一次事件轉(zhuǎn)發(fā),也就是說(shuō)將QQuickPaintedItem
獲得的事件合理的轉(zhuǎn)發(fā)給QWidget讓QWidget能處理對(duì)應(yīng)的消息。這個(gè)可以通過(guò)在QQuickPaintedItem
中過(guò)濾事件后進(jìn)行轉(zhuǎn)發(fā)。
而UI渲染是在paint中調(diào)用widget的rander進(jìn)行,我們知道Widget中UI渲染是在主線程,上面講到 QQuickPaintedItem中的paint()不是從主GUI線程調(diào)用的,而是從啟用GL的渲染器線程調(diào)用的,所以這樣調(diào)用就會(huì)出現(xiàn)斷言報(bào)錯(cuò):
"Cannot send events to objects owned by a different thread. Current thread 0x0x24f6d9d9db0. Receiver ''....
好在我們可以直接用Release
模式規(guī)避這個(gè)斷言,沒(méi)辦法,只能這樣搞了,也只有這種方式能實(shí)現(xiàn)這種非常規(guī)的調(diào)用。
繼承QQuickPaintedItem封裝的關(guān)鍵代碼:
HexViewItem::HexViewItem()
{
this->setAcceptHoverEvents(true);
this->setAcceptedMouseButtons(Qt::AllButtons);
setFlag(ItemAcceptsInputMethod, true);
setFlag(ItemIsFocusScope, true);
setFlag(ItemHasContents, true);
}
//不能在構(gòu)造函數(shù)中調(diào)用
void HexViewItem::init()
{
m_pHexContainer = new HexViewContainer();
m_pHexContainer->init();
qDebug() << __FUNCTION__ << "this size" << size();
m_pHexContainer->resize(this->size().toSize());
if(m_pHexContainer)
{
m_pHexContainer->createWinId();
m_pHexContainer->installEventFilter(this);
QWindow* pw = (QWindow*)(window());
pw->installEventFilter(this);
this->update();
}
}
bool HexViewItem::sendEventToWidget(QEvent *e)
{
if(!m_pHexContainer)return false;
QWindow* wHandle = m_pHexContainer->windowHandle();
bool res = false;
if(wHandle)
{
res = qApp->sendEvent(wHandle, e);
}
return res;
}
void HexViewItem::paint(QPainter *painter)
{
painter->save();
if(m_pHexContainer)
{
m_pHexContainer->render(painter);
}
painter->restore();
}
bool HexViewItem::eventFilter(QObject *obj, QEvent *e)
{
QWindow* pw = (QWindow*)(window());
bool res = QQuickPaintedItem::eventFilter(obj, e);
if(obj == m_pHexContainer)
{
switch(e->type())
{
case QEvent::Paint:
{
QPaintEvent* pe = (QPaintEvent*)e;
this->update(pe->rect());
}
break;
}
}
else if(obj == pw)
{
//* 如果是鼠標(biāo)等(有鼠標(biāo)坐標(biāo)信息的事件。)的話我們得計(jì)算一下偏移量并修正一下,這里就只處理QMouseEvent和QWheelEvent作為示例
//* 如果有其他類似的也需要修正,不然可能坐標(biāo)偏移
switch(e->type())
{
case QEvent::MouseButtonDblClick :
case QEvent::MouseButtonPress :
case QEvent::MouseButtonRelease :
case QEvent::MouseMove :
case QEvent::MouseTrackingChange :
case QEvent::Move :
{
QMouseEvent *me = (QMouseEvent*)e;
QEvent::Type type = me->type();
QPointF localPosF = me->localPos();
Qt::MouseButton mouseButton = me->button();
Qt::MouseButtons mouseButtons = me->buttons();
Qt::KeyboardModifiers modifiers = me->modifiers();
//修正一下localpos
QPointF offsetF = mapToScene(QPoint(0,0));
QPointF diffPosF = localPosF - offsetF;
QMouseEvent tme(type, diffPosF, mouseButton, mouseButtons, modifiers);
sendEventToWidget(&tme);
}
break;
case QEvent::Wheel:
{
QWheelEvent *we = (QWheelEvent*)e;
QPointF localPosF = we->posF();
QPointF gloabalPosF = we->globalPosF();
QPoint pixelDelta = we->pixelDelta();
QPoint angleDelta = we->angleDelta();
int qt4Delta = we->delta();
Qt::Orientation orientation = we->orientation();
Qt::MouseButtons mouseButtons = we->buttons();
Qt::KeyboardModifiers modifiers = we->modifiers();
//修正一下localpos
QPointF offsetF = mapToScene(QPoint(0,0));
QPointF diffPosF = localPosF - offsetF;
QWheelEvent twe(diffPosF, gloabalPosF, pixelDelta, angleDelta, qt4Delta, orientation, mouseButtons, modifiers);
sendEventToWidget(&twe);
}
break;
default:
{
// sendEventToWidget(e);
}
break;
}
}
return res;
}
void HexViewItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry);
if(m_pHexContainer)
{
m_pHexContainer->setGeometry(newGeometry.toRect());
}
}
bool HexViewItem::event(QEvent *e)
{
return __super::event(e);
}
關(guān)鍵點(diǎn),將QQuickItem的窗口注冊(cè)事件過(guò)濾器:
QWindow* pw = (QWindow*)(window());
pw->installEventFilter(this);
eventFilter就是過(guò)濾一些需要用到的關(guān)鍵事件進(jìn)行轉(zhuǎn)發(fā),在paint()中調(diào)用Widget的render進(jìn)行UI刷新。
QHexView源碼進(jìn)行過(guò)一些修改,添加了一些接口可供QML中快速設(shè)置,如:高亮某段數(shù)據(jù)、快速定位,頭部底部對(duì)齊,主題切換,截圖保存,切換展示寬度等
Q_INVOKABLE void setFilePath(int index, QString filePath);
Q_INVOKABLE void updateGeo();
Q_INVOKABLE void setSelect(int index,int pos,int len);
Q_INVOKABLE void closeFile(int index); //關(guān)閉文件
Q_INVOKABLE void alignment(bool head,int index = -1); //頭部 尾部對(duì)齊
Q_INVOKABLE QStringList renderHexView(QList<int> fileIndexs);
Q_INVOKABLE void setTheme(bool darkTheme);
Q_INVOKABLE void setHexLineWidth(quint8 value);
可在此基礎(chǔ)上進(jìn)行擴(kuò)展。
整體目錄結(jié)構(gòu):
本文demo 點(diǎn)擊下載
參考文章:
https://blog.csdn.net/r5014/article/details/92642626
https://github.com/Dax89/QHexView文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-701534.html
到了這里,關(guān)于QML實(shí)現(xiàn)文件十六進(jìn)制數(shù)據(jù)展示的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!