• 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的渲染器线程调用的,所以这样调用就会出现断言报错:

     "Cannot send events to objects owned by a different thread. Current thread 0x0x24f6d9d9db0. Receiver ''....
    
    • 1

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

    继承QQuickPaintedItem封装的关键代码:

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139

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

    QWindow* pw = (QWindow*)(window());
    pw->installEventFilter(this);
    
    • 1
    • 2

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

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

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可在此基础上进行扩展。

    整体目录结构:
    在这里插入图片描述


    本文demo 点击下载


    参考文章:
    https://blog.csdn.net/r5014/article/details/92642626
    https://github.com/Dax89/QHexView

  • 相关阅读:
    矩阵起源加入 OpenCloudOS 操作系统开源社区,完成技术兼容互认证
    不要在Python中使用iterrows()循环
    Linux软件包和进程管理
    基于XML配置的AOP
    IceRPC之使用Dev Containers进行 .NET QUIC 精简开发
    Google Earth Engine(GEE)——一个简单的影像波段时序折现图
    Clickhouse 消除由group by产生的间隙
    创建一个双模式跨运行时的 JavaScript 包
    缓存最佳实践
    Nginx简单使用
  • 原文地址:https://blog.csdn.net/luoyayun361/article/details/132762010