Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
504 views
in Technique[技术] by (71.8m points)

c++ - How can realize my own memory viewer by QT

Recently I am writing a debugger for my own hardware. I want to add a memory viewer widget like the eclipse or qt creator or other IDE. However, I have no idea what kind of widget to use. For example, tableWidget, is there some setting to make it like this:

enter image description here

enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The best option is to create a custom widget because the task is private and none of the widgets are accommodated to the task.

For this we will inherit from QAbstractScrollArea, from this class inherit QTableView, QListView, QListWidget and QTableWidget. It is designed to display data within a QScrollArea. In addition documentation of this class tells us how to create a custom class.

When inheriting QAbstractScrollArea, you need to do the following:

  • Control the scroll bars by setting their range, value, page step, and tracking their movements.
  • Draw the contents of the area in the viewport according to the values of the scroll bars.
  • Handle events received by the viewport in viewportEvent()
  • notably resize events. Use viewport->update() to update the contents of the viewport instead of update() as all painting operations take place on the viewport.

Taking that reference I created the following widget:

memoryviewer.h

#ifndef MEMORYVIEWER_H
#define MEMORYVIEWER_H

#include <QAbstractScrollArea>
#include <QBuffer>

class MemoryViewer : public QAbstractScrollArea
{
    Q_OBJECT
public:
    MemoryViewer(QWidget *parent = 0);
    ~MemoryViewer();

    void setData(const QByteArray &ba);
    bool setData(QIODevice &device);

protected:
    void paintEvent(QPaintEvent *);
    void resizeEvent(QResizeEvent *);

private:
    void adjustContent();
    void init();

    int addressWidth();
    int hexWidth();
    int asciiWidth();

    QByteArray data(qint64 pos=0, qint64 count=-1);

    int nBlockAddress;
    int mBytesPerLine;

    int pxWidth;
    int pxHeight;

    qint64 startPos;
    qint64 endPos;

    int nRowsVisible;

    QBuffer buffer;
    QIODevice *ioDevice;
    qint64 size;

    QByteArray dataVisible;
    QByteArray dataHex;
};

#endif // MEMORYVIEWER_H

memoryviewer.cpp

#include "memoryviewer.h"

#include <QPainter>
#include <QScrollBar>

MemoryViewer::MemoryViewer(QWidget *parent):QAbstractScrollArea(parent)
{
    ioDevice = new QBuffer(this);
    init();
    connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &MemoryViewer::adjustContent);
    connect(horizontalScrollBar(), &QScrollBar::valueChanged, this, &MemoryViewer::adjustContent);
}

MemoryViewer::~MemoryViewer()
{

}


void MemoryViewer::init()
{
    nBlockAddress = 2;
    mBytesPerLine = 16;

    pxWidth = fontMetrics().width(QChar('0'));
    pxHeight = fontMetrics().height();

}

int MemoryViewer::addressWidth()
{
    return  (nBlockAddress*4+ nBlockAddress -1)*pxWidth;
}

int MemoryViewer::hexWidth()
{
    return (mBytesPerLine*3+1)*pxWidth;
}

int MemoryViewer::asciiWidth()
{
    return (mBytesPerLine*2 +1)*pxWidth;
}

QByteArray MemoryViewer::data(qint64 pos, qint64 count)
{
    QByteArray buffer;

    if (pos >= size)
        return buffer;

    if (count < 0)
        count = size;
    else
        if ((pos + count) > size)
            count = size - pos;

    if(ioDevice->open(QIODevice::ReadOnly)){
        ioDevice->seek(pos);
        buffer = ioDevice->read(count);
        ioDevice->close();
    }
    return buffer;
}

void MemoryViewer::setData(const QByteArray &ba)
{
    buffer.setData(ba);
    setData(buffer);

}

bool MemoryViewer::setData(QIODevice &device)
{
    ioDevice = &device;
    bool ok = ioDevice->open(QIODevice::ReadOnly);
    if(ok){
        size = ioDevice->size();
        ioDevice->close();
    }
    else{
        QBuffer *buf = new QBuffer(this);
        ioDevice = buf;
    }
    init();
    adjustContent();
    return ok;
}

void MemoryViewer::resizeEvent(QResizeEvent *)
{
    adjustContent();
}


void MemoryViewer::paintEvent(QPaintEvent *)
{
    QPainter painter(viewport());

    int offsetX = horizontalScrollBar()->value();

    int y = pxHeight;
    QString address;

    painter.setPen(viewport()->palette().color(QPalette::WindowText));

    for(int row = 0; row <= dataVisible.size()/mBytesPerLine;  row++){
        QString str = QString("%1").arg(startPos + mBytesPerLine*row, nBlockAddress*4, 16, QChar('0')).toUpper();
        int i = 0;
        address = "";
        while(i < nBlockAddress){
            address += str.mid(i*4, 4) + ":";
            i++;
        }
        address.remove(address.size()-1, 1);

        painter.drawText(pxWidth/2 -offsetX , y, address);
        y+=pxHeight;
    }

    int x;
    int lx = addressWidth() +pxWidth;
    painter.drawLine(lx-offsetX, 0, lx-offsetX, height());
    lx += pxWidth/2;
    y = pxHeight;

    //hex data
    x = lx-offsetX+3*pxWidth;
    int w = 3*pxWidth;
    for(int col =0; col < mBytesPerLine/2; col++){
        painter.fillRect(x-pxWidth/2, 0, w, height(), viewport()->palette().color(QPalette::AlternateBase));
        x+= 6*pxWidth;
    }

    int bPos = 0;
    for(int row=0; row < nRowsVisible; row++){
        x = lx-offsetX;
        for(int col =0; (col < mBytesPerLine) && (bPos < dataHex.size()) ; col++){
            QString str = dataHex.mid(bPos*2,2).toUpper();
            painter.drawText(x, y, str);
            x += 3*pxWidth;
            bPos += 1;
        }
        y+= pxHeight;
    }

    lx = addressWidth() + hexWidth();
    painter.drawLine(lx-offsetX, 0, lx-offsetX, height());

    lx += pxWidth/2;

    bPos = 0;
    y = pxHeight ;
    int ch;
    for(int row=0; row < nRowsVisible; row++){
        x = lx-offsetX;
        for(int col =0; (col < mBytesPerLine) && (bPos < dataVisible.size()) ; col++){
            ch  = (uchar)dataVisible.at(bPos);
            if ( ch < 0x20 )
                ch = '.';
            painter.drawText(x, y, QChar(ch));
            x += 2*pxWidth;
            bPos += 1;
        }
        y+= pxHeight;
    }
}

void MemoryViewer::adjustContent()
{
    int w = addressWidth() + hexWidth() + asciiWidth();
    horizontalScrollBar()->setRange(0, w - viewport()->width());
    horizontalScrollBar()->setPageStep(viewport()->width());

    nRowsVisible = viewport()->height()/pxHeight;
    int val = verticalScrollBar()->value();
    startPos = (qint64)val*mBytesPerLine;
    endPos = startPos + nRowsVisible*mBytesPerLine -1;

    int lineCount = size/mBytesPerLine;
    verticalScrollBar()->setRange(0,  lineCount-nRowsVisible);
    verticalScrollBar()->setPageStep(nRowsVisible);

    if(endPos >= size){
        endPos = size-1;
    }
    dataVisible = data(startPos, endPos-startPos + mBytesPerLine +1);
    dataHex = dataVisible.toHex();
    viewport()->update();
}

An advantage of the implementation is that you do not have to load all the bytes directly since you can pass an object that inherits from QIODevice such as QFile and read the data when the view needs it by eliminating the memory overhead. In addition you can pass a QByteArray

In the following link you will find an example

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...