QML实现文件十六进制数据展示

前言

将一个二进制文件直接拖放到Qt Creator中可以直接查看到以十六进制显示的数据格式,如:

要实现一个这样的效果,还是要花不少时间的。

在网上找了挺多示例,其中一个开源代码效果不错(参考这里),但是是在QWidget中实现的,通过继承QAbstractScrollArea来实现数据滚动绘制。

如果QML中要自定义在paint中绘制,需要继承QQuickPaintedItem,那要实现这样的滚动分页展示就比较麻烦了。尝试了一下直接将Widget中实现的效果封装一下放到QML中去使用,就会比较省事,毕竟原有的开源效果已经做得很好了。

以此思路,开搞!!!

先来看效果:

这是在QML项目中实现的,中间的数据展示部分是使用QHexView代码Widget实现的。


本文demo 点击下载


正文

将Widget嵌入到QML中使用,先来看会遇到什么问题。

上面说到,QML中要自定义在paint中绘制,需要继承QQuickPaintedItem,然后重写paint函数,而QML Scene Graph场景图绘制是在两个不同的线程中,这个在Qt帮助文档中有说明:

也就是说,paint()不是从主GUI线程调用的,而是从启用GL的渲染器线程调用的.。

那这会带来什么问题呢,继续看

由于主UI框架是用QML做的,我们想把Widget嵌入到QML中使用,就需要新建一个类继承QQuickPaintedItem然后把Widget放到其中,封装起来后将该类通过qmlRegisterType注册交给QML去引用。那Widget窗口中的各种事件就需要从QML这边发送过去,所以需要进行一次事件转发,也就是说将QQuickPaintedItem获得的事件合理的转发给QWidget让QWidget能处理对应的消息。这个可以通过在QQuickPaintedItem中过滤事件后进行转发。

而UI渲染是在paint中调用widget的rander进行,我们知道Widget中UI渲染是在主线程,上面讲到 QQuickPaintedItem中的paint()不是从主GUI线程调用的,而是从启用GL的渲染器线程调用的,所以这样调用就会出现断言报错:

cpp 复制代码
 "Cannot send events to objects owned by a different thread. Current thread 0x0x24f6d9d9db0. Receiver ''....

好在我们可以直接用Release模式规避这个断言,没办法,只能这样搞了,也只有这种方式能实现这种非常规的调用。

继承QQuickPaintedItem封装的关键代码:

cpp 复制代码
HexViewItem::HexViewItem()
{
    this->setAcceptHoverEvents(true);
    this->setAcceptedMouseButtons(Qt::AllButtons);
    setFlag(ItemAcceptsInputMethod, true);
    setFlag(ItemIsFocusScope, true);
    setFlag(ItemHasContents, true);
}

//不能在构造函数中调用
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)
    {
        //* 如果是鼠标等(有鼠标坐标信息的事件。)的话我们得计算一下偏移量并修正一下,这里就只处理QMouseEvent和QWheelEvent作为示例
        //* 如果有其他类似的也需要修正,不然可能坐标偏移
        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);
}

关键点,将QQuickItem的窗口注册事件过滤器:

cpp 复制代码
QWindow* pw = (QWindow*)(window());
pw->installEventFilter(this);

eventFilter就是过滤一些需要用到的关键事件进行转发,在paint()中调用Widget的render进行UI刷新。

QHexView源码进行过一些修改,添加了一些接口可供QML中快速设置,如:高亮某段数据、快速定位,头部底部对齐,主题切换,截图保存,切换展示宽度等

cpp 复制代码
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);  //关闭文件
Q_INVOKABLE void alignment(bool head,int index = -1);  //头部 尾部对齐
Q_INVOKABLE QStringList renderHexView(QList<int> fileIndexs);
Q_INVOKABLE void setTheme(bool darkTheme);
Q_INVOKABLE void setHexLineWidth(quint8 value);

可在此基础上进行扩展。

整体目录结构:


本文demo 点击下载


参考文章:

https://blog.csdn.net/r5014/article/details/92642626

https://github.com/Dax89/QHexView

相关推荐
美好生活丶2 个月前
HexView 刷写文件脚本处理工具-命令行介绍(八)-文件合并(/MO /MT)
单片机·autosar·汽车电子·hexview·刷写文件
緑水長流*z5 个月前
计算机基础(5)——进制与进制转换
二进制·进制转换·十六进制·计算机基础·十进制·进制运算
南风与鱼1 年前
文件操作入门指南
c语言·文件·二进制文件·文本文件
一颗不甘坠落的流星1 年前
【CSS】各百分比透明度 opacity 对应的 16 进制颜色值(例如:#FFFFFF80)
前端·css·十六进制·opacity·16进制
BruceGerGer1 年前
flutter开发实战-颜色Color与16进制转换
flutter·hex·color·十六进制
dvlinker1 年前
使用反汇编工具IDA查看发生异常的汇编代码的上下文去辅助分析C++软件异常
反汇编工具·汇编代码·排查软件异常·二进制机器码·二进制文件