当前位置: 首页 > news >正文

Qt中显示hex数据的控件

效果类似QTextEdit,但是显示十六进制的数据,比如用于显示抓取串口或者bin文件的数据等等

chunks.h

#ifndef CHUNKS_H
#define CHUNKS_H/** \cond docNever *//*! The Chunks class is the storage backend for QHexEdit.** When QHexEdit loads data, Chunks access them using a QIODevice interface. When the app uses* a QByteArray interface, QBuffer is used to provide again a QIODevice like interface. No data* will be changed, therefore Chunks opens the QIODevice in QIODevice::ReadOnly mode. After every* access Chunks closes the QIODevice, that's why external applications can overwrite files while* QHexEdit shows them.** When the the user starts to edit the data, Chunks creates a local copy of a chunk of data (4* kilobytes) and notes all changes there. Parallel to that chunk, there is a second chunk,* which keep track of which bytes are changed and which not.**/#include <QtCore>struct Chunk
{QByteArray data;QByteArray dataChanged;qint64 absPos;
};class Chunks: public QObject
{
Q_OBJECT
public:// Constructors and file settingsChunks(QObject *parent);Chunks(QIODevice &ioDevice, QObject *parent);bool setIODevice(QIODevice &ioDevice);// Getting data out of ChunksQByteArray data(qint64 pos=0, qint64 count=-1, QByteArray *highlighted=0);bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1);// Set and get highlighting infosvoid setDataChanged(qint64 pos, bool dataChanged);bool dataChanged(qint64 pos);// Search APIqint64 indexOf(const QByteArray &ba, qint64 from);qint64 lastIndexOf(const QByteArray &ba, qint64 from);// Char manipulationsbool insert(qint64 pos, char b);bool overwrite(qint64 pos, char b);bool removeAt(qint64 pos);// Utility functionschar operator[](qint64 pos);qint64 pos();qint64 size();private:int getChunkIndex(qint64 absPos);QIODevice * _ioDevice;qint64 _pos;qint64 _size;QList<Chunk> _chunks;#ifdef MODUL_TEST
public:int chunkSize();
#endif
};/** \endcond docNever */#endif // CHUNKS_H

chunks.cpp

#include "chunks.h"
#include <limits.h>#define NORMAL 0
#define HIGHLIGHTED 1#define BUFFER_SIZE 0x10000
#define CHUNK_SIZE 0x1000
#define READ_CHUNK_MASK Q_INT64_C(0xfffffffffffff000)// ***************************************** Constructors and file settingsChunks::Chunks(QObject *parent): QObject(parent)
{QBuffer *buf = new QBuffer(this);setIODevice(*buf);
}Chunks::Chunks(QIODevice &ioDevice, QObject *parent): QObject(parent)
{setIODevice(ioDevice);
}bool Chunks::setIODevice(QIODevice &ioDevice)
{_ioDevice = &ioDevice;bool ok = _ioDevice->open(QIODevice::ReadOnly);if (ok)   // Try to open IODevice{_size = _ioDevice->size();_ioDevice->close();}else                                        // Fallback is an empty buffer{QBuffer *buf = new QBuffer(this);_ioDevice = buf;_size = 0;}_chunks.clear();_pos = 0;return ok;
}// ***************************************** Getting data out of ChunksQByteArray Chunks::data(qint64 pos, qint64 maxSize, QByteArray *highlighted)
{qint64 ioDelta = 0;int chunkIdx = 0;Chunk chunk;QByteArray buffer;// Do some checks and some arrangementsif (highlighted)highlighted->clear();if (pos >= _size)return buffer;if (maxSize < 0)maxSize = _size;elseif ((pos + maxSize) > _size)maxSize = _size - pos;_ioDevice->open(QIODevice::ReadOnly);while (maxSize > 0){chunk.absPos = LLONG_MAX;bool chunksLoopOngoing = true;while ((chunkIdx < _chunks.count()) && chunksLoopOngoing){// In this section, we track changes before our required data and// we take the editdet data, if availible. ioDelta is a difference// counter to justify the read pointer to the original data, if// data in between was deleted or inserted.chunk = _chunks[chunkIdx];if (chunk.absPos > pos)chunksLoopOngoing = false;else{chunkIdx += 1;qint64 count;qint64 chunkOfs = pos - chunk.absPos;if (maxSize > ((qint64)chunk.data.size() - chunkOfs)){count = (qint64)chunk.data.size() - chunkOfs;ioDelta += CHUNK_SIZE - chunk.data.size();}elsecount = maxSize;if (count > 0){buffer += chunk.data.mid(chunkOfs, (int)count);maxSize -= count;pos += count;if (highlighted)*highlighted += chunk.dataChanged.mid(chunkOfs, (int)count);}}}if ((maxSize > 0) && (pos < chunk.absPos)){// In this section, we read data from the original source. This only will// happen, whe no copied data is availableqint64 byteCount;QByteArray readBuffer;if ((chunk.absPos - pos) > maxSize)byteCount = maxSize;elsebyteCount = chunk.absPos - pos;maxSize -= byteCount;_ioDevice->seek(pos + ioDelta);readBuffer = _ioDevice->read(byteCount);buffer += readBuffer;if (highlighted)*highlighted += QByteArray(readBuffer.size(), NORMAL);pos += readBuffer.size();}}_ioDevice->close();return buffer;
}bool Chunks::write(QIODevice &iODevice, qint64 pos, qint64 count)
{if (count == -1)count = _size;bool ok = iODevice.open(QIODevice::WriteOnly);if (ok){for (qint64 idx=pos; idx < count; idx += BUFFER_SIZE){QByteArray ba = data(idx, BUFFER_SIZE);iODevice.write(ba);}iODevice.close();}return ok;
}// ***************************************** Set and get highlighting infosvoid Chunks::setDataChanged(qint64 pos, bool dataChanged)
{if ((pos < 0) || (pos >= _size))return;int chunkIdx = getChunkIndex(pos);qint64 posInBa = pos - _chunks[chunkIdx].absPos;_chunks[chunkIdx].dataChanged[(int)posInBa] = char(dataChanged);
}bool Chunks::dataChanged(qint64 pos)
{QByteArray highlighted;data(pos, 1, &highlighted);return bool(highlighted.at(0));
}// ***************************************** Search APIqint64 Chunks::indexOf(const QByteArray &ba, qint64 from)
{qint64 result = -1;QByteArray buffer;for (qint64 pos=from; (pos < _size) && (result < 0); pos += BUFFER_SIZE){buffer = data(pos, BUFFER_SIZE + ba.size() - 1);int findPos = buffer.indexOf(ba);if (findPos >= 0)result = pos + (qint64)findPos;}return result;
}qint64 Chunks::lastIndexOf(const QByteArray &ba, qint64 from)
{qint64 result = -1;QByteArray buffer;for (qint64 pos=from; (pos > 0) && (result < 0); pos -= BUFFER_SIZE){qint64 sPos = pos - BUFFER_SIZE - (qint64)ba.size() + 1;if (sPos < 0)sPos = 0;buffer = data(sPos, pos - sPos);int findPos = buffer.lastIndexOf(ba);if (findPos >= 0)result = sPos + (qint64)findPos;}return result;
}// ***************************************** Char manipulationsbool Chunks::insert(qint64 pos, char b)
{if ((pos < 0) || (pos > _size))return false;int chunkIdx;if (pos == _size)chunkIdx = getChunkIndex(pos-1);elsechunkIdx = getChunkIndex(pos);qint64 posInBa = pos - _chunks[chunkIdx].absPos;_chunks[chunkIdx].data.insert(posInBa, b);_chunks[chunkIdx].dataChanged.insert(posInBa, char(1));for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)_chunks[idx].absPos += 1;_size += 1;_pos = pos;return true;
}bool Chunks::overwrite(qint64 pos, char b)
{if ((pos < 0) || (pos >= _size))return false;int chunkIdx = getChunkIndex(pos);qint64 posInBa = pos - _chunks[chunkIdx].absPos;_chunks[chunkIdx].data[(int)posInBa] = b;_chunks[chunkIdx].dataChanged[(int)posInBa] = char(1);_pos = pos;return true;
}bool Chunks::removeAt(qint64 pos)
{if ((pos < 0) || (pos >= _size))return false;int chunkIdx = getChunkIndex(pos);qint64 posInBa = pos - _chunks[chunkIdx].absPos;_chunks[chunkIdx].data.remove(posInBa, 1);_chunks[chunkIdx].dataChanged.remove(posInBa, 1);for (int idx=chunkIdx+1; idx < _chunks.size(); idx++)_chunks[idx].absPos -= 1;_size -= 1;_pos = pos;return true;
}// ***************************************** Utility functionschar Chunks::operator[](qint64 pos)
{return data(pos, 1)[0];
}qint64 Chunks::pos()
{return _pos;
}qint64 Chunks::size()
{return _size;
}int Chunks::getChunkIndex(qint64 absPos)
{// This routine checks, if there is already a copied chunk available. If os, it// returns a reference to it. If there is no copied chunk available, original// data will be copied into a new chunk.int foundIdx = -1;int insertIdx = 0;qint64 ioDelta = 0;for (int idx=0; idx < _chunks.size(); idx++){Chunk chunk = _chunks[idx];if ((absPos >= chunk.absPos) && (absPos < (chunk.absPos + chunk.data.size()))){foundIdx = idx;break;}if (absPos < chunk.absPos){insertIdx = idx;break;}ioDelta += chunk.data.size() - CHUNK_SIZE;insertIdx = idx + 1;}if (foundIdx == -1){Chunk newChunk;qint64 readAbsPos = absPos - ioDelta;qint64 readPos = (readAbsPos & READ_CHUNK_MASK);_ioDevice->open(QIODevice::ReadOnly);_ioDevice->seek(readPos);newChunk.data = _ioDevice->read(CHUNK_SIZE);_ioDevice->close();newChunk.absPos = absPos - (readAbsPos - readPos);newChunk.dataChanged = QByteArray(newChunk.data.size(), char(0));_chunks.insert(insertIdx, newChunk);foundIdx = insertIdx;}return foundIdx;
}#ifdef MODUL_TEST
int Chunks::chunkSize()
{return _chunks.size();
}#endif

commands.h

#ifndef COMMANDS_H
#define COMMANDS_H/** \cond docNever */#include <QUndoStack>#include "chunks.h"/*! CharCommand is a class to provid undo/redo functionality in QHexEdit.
A QUndoCommand represents a single editing action on a document. CharCommand
is responsable for manipulations on single chars. It can insert. overwrite and
remove characters. A manipulation stores allways two actions
1. redo (or do) action
2. undo action.CharCommand also supports command compression via mergeWidht(). This enables
the user to perform an undo command e.g. 3 steps in a single command.
If you for example insert a new byt "34" this means for the editor doing 3
steps: insert a "00", overwrite it with "03" and the overwrite it with "34". These
3 steps are combined into a single step, insert a "34".The byte array oriented commands are just put into a set of single byte commands,
which are pooled together with the macroBegin() and macroEnd() functionality of
Qt's QUndoStack.
*/class UndoStack : public QUndoStack
{Q_OBJECTpublic:UndoStack(Chunks *chunks, QObject * parent=0);void insert(qint64 pos, char c);void insert(qint64 pos, const QByteArray &ba);void removeAt(qint64 pos, qint64 len=1);void overwrite(qint64 pos, char c);void overwrite(qint64 pos, int len, const QByteArray &ba);private:Chunks * _chunks;QObject * _parent;
};/** \endcond docNever */#endif // COMMANDS_H

commands.cpp

#include "commands.h"
#include <QUndoCommand>// Helper class to store single byte commands
class CharCommand : public QUndoCommand
{
public:enum CCmd {insert, removeAt, overwrite};CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar,QUndoCommand *parent=0);void undo();void redo();bool mergeWith(const QUndoCommand *command);int id() const { return 1234; }private:Chunks * _chunks;qint64 _charPos;bool _wasChanged;char _newChar;char _oldChar;CCmd _cmd;
};CharCommand::CharCommand(Chunks * chunks, CCmd cmd, qint64 charPos, char newChar, QUndoCommand *parent): QUndoCommand(parent)
{_chunks = chunks;_charPos = charPos;_newChar = newChar;_cmd = cmd;
}bool CharCommand::mergeWith(const QUndoCommand *command)
{const CharCommand *nextCommand = static_cast<const CharCommand *>(command);bool result = false;if (_cmd != CharCommand::removeAt){if (nextCommand->_cmd == overwrite)if (nextCommand->_charPos == _charPos){_newChar = nextCommand->_newChar;result = true;}}return result;
}void CharCommand::undo()
{switch (_cmd){case insert:_chunks->removeAt(_charPos);break;case overwrite:_chunks->overwrite(_charPos, _oldChar);_chunks->setDataChanged(_charPos, _wasChanged);break;case removeAt:_chunks->insert(_charPos, _oldChar);_chunks->setDataChanged(_charPos, _wasChanged);break;}
}void CharCommand::redo()
{switch (_cmd){case insert:_chunks->insert(_charPos, _newChar);break;case overwrite:_oldChar = (*_chunks)[_charPos];_wasChanged = _chunks->dataChanged(_charPos);_chunks->overwrite(_charPos, _newChar);break;case removeAt:_oldChar = (*_chunks)[_charPos];_wasChanged = _chunks->dataChanged(_charPos);_chunks->removeAt(_charPos);break;}
}UndoStack::UndoStack(Chunks * chunks, QObject * parent): QUndoStack(parent)
{_chunks = chunks;_parent = parent;this->setUndoLimit(1000);
}void UndoStack::insert(qint64 pos, char c)
{if ((pos >= 0) && (pos <= _chunks->size())){QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos, c);this->push(cc);}
}void UndoStack::insert(qint64 pos, const QByteArray &ba)
{if ((pos >= 0) && (pos <= _chunks->size())){QString txt = QString(tr("Inserting %1 bytes")).arg(ba.size());beginMacro(txt);for (int idx=0; idx < ba.size(); idx++){QUndoCommand *cc = new CharCommand(_chunks, CharCommand::insert, pos + idx, ba.at(idx));this->push(cc);}endMacro();}
}void UndoStack::removeAt(qint64 pos, qint64 len)
{if ((pos >= 0) && (pos < _chunks->size())){if (len==1){QUndoCommand *cc = new CharCommand(_chunks, CharCommand::removeAt, pos, char(0));this->push(cc);}else{QString txt = QString(tr("Delete %1 chars")).arg(len);beginMacro(txt);for (qint64 cnt=0; cnt<len; cnt++){QUndoCommand *cc = new CharCommand(_chunks, CharCommand::removeAt, pos, char(0));push(cc);}endMacro();}}
}void UndoStack::overwrite(qint64 pos, char c)
{if ((pos >= 0) && (pos < _chunks->size())){QUndoCommand *cc = new CharCommand(_chunks, CharCommand::overwrite, pos, c);this->push(cc);}
}void UndoStack::overwrite(qint64 pos, int len, const QByteArray &ba)
{if ((pos >= 0) && (pos < _chunks->size())){QString txt = QString(tr("Overwrite %1 chars")).arg(len);beginMacro(txt);removeAt(pos, len);insert(pos, ba);endMacro();}
}

qhexedit.h

#ifndef QHEXEDIT_H
#define QHEXEDIT_H#include <QAbstractScrollArea>
#include <QPen>
#include <QBrush>#include "chunks.h"
#include "commands.h"#ifdef QHEXEDIT_EXPORTS
#define QHEXEDIT_API Q_DECL_EXPORT
#elif QHEXEDIT_IMPORTS
#define QHEXEDIT_API Q_DECL_IMPORT
#else
#define QHEXEDIT_API
#endif/** \mainpage
QHexEdit is a binary editor widget for Qt.
*//** QHexEdit is a hex editor widget written in C++ for the Qt (Qt4, Qt5) framework.
It is a simple editor for binary data, just like QPlainTextEdit is for text
data. There are sip configuration files included, so it is easy to create
bindings for PyQt and you can use this widget also in python 2 and 3.QHexEdit takes the data of a QByteArray (setData()) and shows it. You can use
the mouse or the keyboard to navigate inside the widget. If you hit the keys
(0..9, a..f) you will change the data. Changed data is highlighted and can be
accessed via data().Normally QHexEdit works in the overwrite mode. You can set overwrite mode(false)
and insert data. In this case the size of data() increases. It is also possible
to delete bytes (del or backspace), here the size of data decreases.You can select data with keyboard hits or mouse movements. The copy-key will
copy the selected data into the clipboard. The cut-key copies also but deletes
it afterwards. In overwrite mode, the paste function overwrites the content of
the (does not change the length) data. In insert mode, clipboard data will be
inserted. The clipboard content is expected in ASCII Hex notation. Unknown
characters will be ignored.QHexEdit comes with undo/redo functionality. All changes can be undone, by
pressing the undo-key (usually ctr-z). They can also be redone afterwards.
The undo/redo framework is cleared, when setData() sets up a new
content for the editor. You can search data inside the content with indexOf()
and lastIndexOf(). The replace() function is to change located subdata. This
'replaced' data can also be undone by the undo/redo framework.QHexEdit is based on QIODevice, that's why QHexEdit can handle big amounts of
data. The size of edited data can be more then two gigabytes without any
restrictions.
*/class QHEXEDIT_API QHexEdit : public QAbstractScrollArea
{Q_OBJECT/*! Property address area switch the address area on or off. Set addressArea true(show it), false (hide it).*/Q_PROPERTY(bool addressArea READ addressArea WRITE setAddressArea)/*! Property address area color sets (setAddressAreaColor()) the backgroundcolor of address areas. You can also read the color (addressAreaColor()).*/Q_PROPERTY(QColor addressAreaColor READ addressAreaColor WRITE setAddressAreaColor)/*! Property addressOffset is added to the Numbers of the Address Area.A offset in the address area (left side) is sometimes useful, whe you showonly a segment of a complete memory picture. With setAddressOffset() you setthis property - with addressOffset() you get the current value.*/Q_PROPERTY(qint64 addressOffset READ addressOffset WRITE setAddressOffset)/*! Set and get the minimum width of the address area, width in characters.*/Q_PROPERTY(int addressWidth READ addressWidth WRITE setAddressWidth)/*! Switch the ascii area on (true, show it) or off (false, hide it).*/Q_PROPERTY(bool asciiArea READ asciiArea WRITE setAsciiArea)/*! Set and get bytes number per line.*/Q_PROPERTY(int bytesPerLine READ bytesPerLine WRITE setBytesPerLine)/*! Property cursorPosition sets or gets the position of the editor cursorin QHexEdit. Every byte in data has two cursor positions: the lower and upperNibble. Maximum cursor position is factor two of data.size().*/Q_PROPERTY(qint64 cursorPosition READ cursorPosition WRITE setCursorPosition)/*! Property data holds the content of QHexEdit. Call setData() to set thecontent of QHexEdit, data() returns the actual content. When calling setData()with a QByteArray as argument, QHexEdit creates a internal copy of the dataIf you want to edit big files please use setData(), based on QIODevice.*/Q_PROPERTY(QByteArray data READ data WRITE setData NOTIFY dataChanged)/*! That property defines if the hex values looks as a-f if the value is false(default)or A-F if value is true.*/Q_PROPERTY(bool hexCaps READ hexCaps WRITE setHexCaps)/*! Property defines the dynamic calculation of bytesPerLine parameter depends of width of widget.set this property true to avoid horizontal scrollbars and show the maximal possible data. defalut value is false*/Q_PROPERTY(bool dynamicBytesPerLine READ dynamicBytesPerLine WRITE setDynamicBytesPerLine)/*! Switch the highlighting feature on or of: true (show it), false (hide it).*/Q_PROPERTY(bool highlighting READ highlighting WRITE setHighlighting)/*! Property highlighting color sets (setHighlightingColor()) the backgroundcolor of highlighted text areas. You can also read the color(highlightingColor()).*/Q_PROPERTY(QColor highlightingColor READ highlightingColor WRITE setHighlightingColor)/*! Property overwrite mode sets (setOverwriteMode()) or gets (overwriteMode()) the modein which the editor works. In overwrite mode the user will overwrite existing data. Thesize of data will be constant. In insert mode the size will grow, when insertingnew data.*/Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode)/*! Property selection color sets (setSelectionColor()) the backgroundcolor of selected text areas. You can also read the color(selectionColor()).*/Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor)/*! Property readOnly sets (setReadOnly()) or gets (isReadOnly) the modein which the editor works. In readonly mode the the user can only navigatethrough the data and select data; modifying is not possible. Thisproperty's default is false.*/Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)/*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/Q_PROPERTY(QFont font READ font WRITE setFont)public:/*! Creates an instance of QHexEdit.\param parent Parent widget of QHexEdit.*/QHexEdit(QWidget *parent=0);// Access to data of qhexedit/*! Sets the data of QHexEdit. The QIODevice will be opened just before readingand closed immediately afterwards. This is to allow other programs to rewritethe file while editing it.*/bool setData(QIODevice &iODevice);/*! Gives back the data as a QByteArray starting at position \param pos anddelivering \param count bytes.*/QByteArray dataAt(qint64 pos, qint64 count=-1);/*! Gives back the data into a \param iODevice starting at position \param posand delivering \param count bytes.*/bool write(QIODevice &iODevice, qint64 pos=0, qint64 count=-1);// Char handling/*! Inserts a char.\param pos Index position, where to insert\param ch Char, which is to insertThe char will be inserted and size of data grows.*/void insert(qint64 pos, char ch);/*! Removes len bytes from the content.\param pos Index position, where to remove\param len Amount of bytes to remove*/void remove(qint64 pos, qint64 len=1);/*! Replaces a char.\param pos Index position, where to overwrite\param ch Char, which is to insertThe char will be overwritten and size remains constant.*/void replace(qint64 pos, char ch);// ByteArray handling/*! Inserts a byte array.\param pos Index position, where to insert\param ba QByteArray, which is to insertThe QByteArray will be inserted and size of data grows.*/void insert(qint64 pos, const QByteArray &ba);/*! Replaces \param len bytes with a byte array \param ba\param pos Index position, where to overwrite\param ba QByteArray, which is inserted\param len count of bytes to overwriteThe data is overwritten and size of data may change.*/void replace(qint64 pos, qint64 len, const QByteArray &ba);// Utility functions/*! Calc cursor position from graphics position* \param point from where the cursor position should be calculated* \return Cursor position*/qint64 cursorPosition(QPoint point);/*! Ensure the cursor to be visbile*/void ensureVisible();/*! Find first occurrence of ba in QHexEdit data* \param ba Data to find* \param from Point where the search starts* \return pos if fond, else -1*/qint64 indexOf(const QByteArray &ba, qint64 from);/*! Returns if any changes where done on document* \return true when document is modified else false*/bool isModified();/*! Find last occurrence of ba in QHexEdit data* \param ba Data to find* \param from Point where the search starts* \return pos if fond, else -1*/qint64 lastIndexOf(const QByteArray &ba, qint64 from);/*! Gives back a formatted image of the selected content of QHexEdit*/QString selectionToReadableString();/*! Return the selected content of QHexEdit as QByteArray*/QString selectedData();/*! Set Font of QHexEdit* \param font*/void setFont(const QFont &font);/*! Gives back a formatted image of the content of QHexEdit*/QString toReadableString();public slots:/*! Redoes the last operation. If there is no operation to redo, i.e.there is no redo step in the undo/redo history, nothing happens.*/void redo();/*! Undoes the last operation. If there is no operation to undo, i.e.there is no undo step in the undo/redo history, nothing happens.*/void undo();signals:/*! Contains the address, where the cursor is located. */void currentAddressChanged(qint64 address);/*! Contains the size of the data to edit. */void currentSizeChanged(qint64 size);/*! The signal is emitted every time, the data is changed. */void dataChanged();/*! The signal is emitted every time, the overwrite mode is changed. */void overwriteModeChanged(bool state);/*! \cond docNever */
public:~QHexEdit();// Propertiesbool addressArea();void setAddressArea(bool addressArea);QColor addressAreaColor();void setAddressAreaColor(const QColor &color);QColor addressAreaPenColor();void setAddressAreaPenColor(const QColor& color);QColor dataAreaPenColor();void setDataAreaPenColor(const QColor& color);qint64 addressOffset();void setAddressOffset(qint64 addressArea);int addressWidth();void setAddressWidth(int addressWidth);bool asciiArea();void setAsciiArea(bool asciiArea);int bytesPerLine();void setBytesPerLine(int count);qint64 cursorPosition();void setCursorPosition(qint64 position);QByteArray data();void setData(const QByteArray &ba);void appenData(const QByteArray &ba);void setHexCaps(const bool isCaps);bool hexCaps();void setDynamicBytesPerLine(const bool isDynamic);bool dynamicBytesPerLine();bool highlighting();void setHighlighting(bool mode);QColor highlightingColor();void setHighlightingColor(const QColor &color);bool overwriteMode();void setOverwriteMode(bool overwriteMode);bool isReadOnly();void setReadOnly(bool readOnly);QColor selectionColor();void setSelectionColor(const QColor &color);protected:// Handle eventsvoid keyPressEvent(QKeyEvent *event);void mouseMoveEvent(QMouseEvent * event);void mousePressEvent(QMouseEvent * event);void paintEvent(QPaintEvent *event);void resizeEvent(QResizeEvent *);virtual bool focusNextPrevChild(bool next);private:// Handle selectionsvoid resetSelection(qint64 pos);            // set selectionStart and selectionEnd to posvoid resetSelection();                      // set selectionEnd to selectionStartvoid setSelection(qint64 pos);              // set min (if below init) or max (if greater init)qint64 getSelectionBegin();qint64 getSelectionEnd();// Private utility functionsvoid init();void readBuffers();QString toReadable(const QByteArray &ba);private slots:void adjust();                              // recalc pixel positionsvoid dataChangedPrivate(int idx=0);         // emit dataChanged() signalvoid refresh();                             // ensureVisible() and readBuffers()void updateCursor();                        // update blinking cursorprivate:// Name convention: pixel positions start with _pxint _pxCharWidth, _pxCharHeight;            // char dimensions (dependend on font)int _pxPosHexX;                             // X-Pos of HeaxAreaint _pxPosAdrX;                             // X-Pos of Address Areaint _pxPosAsciiX;                           // X-Pos of Ascii Areaint _pxGapAdr;                              // gap left from AddressAreaint _pxGapAdrHex;                           // gap between AddressArea and HexAereaint _pxGapHexAscii;                         // gap between HexArea and AsciiAreaint _pxCursorWidth;                         // cursor widthint _pxSelectionSub;                        // offset selection rectint _pxCursorX;                             // current cursor posint _pxCursorY;                             // current cursor pos// Name convention: absolute byte positions in chunks start with _bqint64 _bSelectionBegin;                    // first position of Selectionqint64 _bSelectionEnd;                      // end of Selectionqint64 _bSelectionInit;                     // memory position of Selectionqint64 _bPosFirst;                          // position of first byte shownqint64 _bPosLast;                           // position of last byte shownqint64 _bPosCurrent;                        // current position// variables to store the property valuesbool _addressArea;                          // left area of QHexEditQColor _addressAreaColor;QColor _addressAreaPenColor;QColor _dataAreaPenColor;int _addressWidth;bool _asciiArea;qint64 _addressOffset;int _bytesPerLine;int _hexCharsInLine;bool _highlighting;bool _overwriteMode;QBrush _brushSelection;QPen _penSelection;QBrush _brushHighlighted;QPen _penHighlighted;bool _readOnly;bool _hexCaps;bool _dynamicBytesPerLine;// other variablesbool _editAreaIsAscii;                      // flag about the ascii mode editedint _addrDigits;                            // real no of addressdigits, may be > addressWidthbool _blink;                                // help get cursor blinkingQBuffer _bData;                             // buffer, when setup with QByteArrayChunks *_chunks;                            // IODevice based access to dataQTimer _cursorTimer;                        // for blinking cursorqint64 _cursorPosition;                     // absolute position of cursor, 1 Byte == 2 ticsQRect _cursorRect;                          // physical dimensions of cursorQByteArray _data;                           // QHexEdit's data, when setup with QByteArrayQByteArray _dataShown;                      // data in the current ViewQByteArray _hexDataShown;                   // data in view, transformed to hexqint64 _lastEventSize;                      // size, which was emitted last timeQByteArray _markedShown;                    // marked data in viewbool _modified;                             // Is any data in editor modified?int _rowsShown;                             // lines of text shownUndoStack * _undoStack;                     // Stack to store edit actions for undo/redo/*! \endcond docNever */
};#endif // QHEXEDIT_H

qhexedit.cpp

#include <QApplication>
#include <QClipboard>
#include <QKeyEvent>
#include <QPainter>
#include <QScrollBar>#include "qhexedit.h"
#include <algorithm>QHexEdit::QHexEdit(QWidget *parent) : QAbstractScrollArea(parent)
{_addressArea = true;_addressWidth = 4;_asciiArea = true;_overwriteMode = true;_highlighting = true;_readOnly = false;_cursorPosition = 0;_lastEventSize = 0;_hexCharsInLine = 47;_bytesPerLine = 16;_editAreaIsAscii = false;_hexCaps = true;_dynamicBytesPerLine = false;_chunks = new Chunks(this);_undoStack = new UndoStack(_chunks, this);#ifdef Q_OS_WIN32setFont(QFont("Courier", 10, 75));
#elsesetFont(QFont("Monospace", 10));
#endifsetAddressAreaColor(this->palette().alternateBase().color());setHighlightingColor(QColor(0xff, 0xff, 0x99, 0xff));setSelectionColor(this->palette().highlight().color());connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor()));connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust()));connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust()));connect(_undoStack, SIGNAL(indexChanged(int)), this, SLOT(dataChangedPrivate(int)));_cursorTimer.setInterval(500);_cursorTimer.start();setAddressWidth(4);setAddressArea(true);setAsciiArea(true);setOverwriteMode(true);setHighlighting(true);setReadOnly(false);init();
}QHexEdit::~QHexEdit()
{}// ********************************************************************** Propertiesvoid QHexEdit::setAddressArea(bool addressArea)
{_addressArea = addressArea;adjust();setCursorPosition(_cursorPosition);viewport()->update();
}bool QHexEdit::addressArea()
{return _addressArea;
}void QHexEdit::setAddressAreaColor(const QColor &color)
{_addressAreaColor = color;viewport()->update();
}QColor QHexEdit::addressAreaPenColor()
{return _addressAreaPenColor;
}void QHexEdit::setAddressAreaPenColor(const QColor &color)
{_addressAreaPenColor = color;viewport()->update();
}QColor QHexEdit::dataAreaPenColor()
{return _dataAreaPenColor;
}void QHexEdit::setDataAreaPenColor(const QColor &color)
{_dataAreaPenColor = color;viewport()->update();
}QColor QHexEdit::addressAreaColor()
{return _addressAreaColor;
}void QHexEdit::setAddressOffset(qint64 addressOffset)
{_addressOffset = addressOffset;adjust();setCursorPosition(_cursorPosition);viewport()->update();
}qint64 QHexEdit::addressOffset()
{return _addressOffset;
}void QHexEdit::setAddressWidth(int addressWidth)
{_addressWidth = addressWidth;adjust();setCursorPosition(_cursorPosition);viewport()->update();
}int QHexEdit::addressWidth()
{qint64 size = _chunks->size();int n = 1;if (size > Q_INT64_C(0x100000000)){ n += 8; size /= Q_INT64_C(0x100000000);}if (size > 0x10000){ n += 4; size /= 0x10000;}if (size > 0x100){ n += 2; size /= 0x100;}if (size > 0x10){ n += 1;}if (n > _addressWidth)return n;elsereturn _addressWidth;
}void QHexEdit::setAsciiArea(bool asciiArea)
{if (!asciiArea)_editAreaIsAscii = false;_asciiArea = asciiArea;adjust();setCursorPosition(_cursorPosition);viewport()->update();
}bool QHexEdit::asciiArea()
{return _asciiArea;
}void QHexEdit::setBytesPerLine(int count)
{_bytesPerLine = count;_hexCharsInLine = count * 3 - 1;adjust();setCursorPosition(_cursorPosition);viewport()->update();
}int QHexEdit::bytesPerLine()
{return _bytesPerLine;
}void QHexEdit::setCursorPosition(qint64 position)
{// 1. delete old cursor_blink = false;viewport()->update(_cursorRect);// 2. Check, if cursor in range?if (position > (_chunks->size() * 2 - 1))position = _chunks->size() * 2  - (_overwriteMode ? 1 : 0);if (position < 0)position = 0;// 3. Calc new position of cursor_bPosCurrent = position / 2;_pxCursorY = ((position / 2 - _bPosFirst) / _bytesPerLine + 1) * _pxCharHeight;int x = (position % (2 * _bytesPerLine));if (_editAreaIsAscii){_pxCursorX = x / 2 * _pxCharWidth + _pxPosAsciiX;_cursorPosition = position & 0xFFFFFFFFFFFFFFFE;}else{_pxCursorX = (((x / 2) * 3) + (x % 2)) * _pxCharWidth + _pxPosHexX;_cursorPosition = position;}if (_overwriteMode)_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY + _pxCursorWidth, _pxCharWidth, _pxCursorWidth);else_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY - _pxCharHeight + 4, _pxCursorWidth, _pxCharHeight);// 4. Immediately draw new cursor_blink = true;viewport()->update(_cursorRect);emit currentAddressChanged(_bPosCurrent);
}qint64 QHexEdit::cursorPosition(QPoint pos)
{// Calc cursor position depending on a graphical positionqint64 result = -1;int posX = pos.x() + horizontalScrollBar()->value();int posY = pos.y() - 3;if ((posX >= _pxPosHexX) && (posX < (_pxPosHexX + (1 + _hexCharsInLine) * _pxCharWidth))){_editAreaIsAscii = false;int x = (posX - _pxPosHexX) / _pxCharWidth;x = (x / 3) * 2 + x % 3;int y = (posY / _pxCharHeight) * 2 * _bytesPerLine;result = _bPosFirst * 2 + x + y;}elseif (_asciiArea && (posX >= _pxPosAsciiX) && (posX < (_pxPosAsciiX + (1 + _bytesPerLine) * _pxCharWidth))){_editAreaIsAscii = true;int x = 2 * (posX - _pxPosAsciiX) / _pxCharWidth;int y = (posY / _pxCharHeight) * 2 * _bytesPerLine;result = _bPosFirst * 2 + x + y;}return result;
}qint64 QHexEdit::cursorPosition()
{return _cursorPosition;
}void QHexEdit::setData(const QByteArray &ba)
{_data = ba;_bData.setData(_data);setData(_bData);
}void QHexEdit::appenData(const QByteArray &ba)
{_data.append(ba);_bData.setData(_data);setData(_bData);
}QByteArray QHexEdit::data()
{return _chunks->data(0, -1);
}void QHexEdit::setHighlighting(bool highlighting)
{_highlighting = highlighting;viewport()->update();
}bool QHexEdit::highlighting()
{return _highlighting;
}void QHexEdit::setHighlightingColor(const QColor &color)
{_brushHighlighted = QBrush(color);_penHighlighted = QPen(viewport()->palette().color(QPalette::WindowText));viewport()->update();
}QColor QHexEdit::highlightingColor()
{return _brushHighlighted.color();
}void QHexEdit::setOverwriteMode(bool overwriteMode)
{_overwriteMode = overwriteMode;emit overwriteModeChanged(overwriteMode);
}bool QHexEdit::overwriteMode()
{return _overwriteMode;
}void QHexEdit::setSelectionColor(const QColor &color)
{_brushSelection = QBrush(color);_penSelection = QPen(Qt::white);viewport()->update();
}QColor QHexEdit::selectionColor()
{return _brushSelection.color();
}bool QHexEdit::isReadOnly()
{return _readOnly;
}void QHexEdit::setReadOnly(bool readOnly)
{_readOnly = readOnly;
}void QHexEdit::setHexCaps(const bool isCaps)
{if (_hexCaps != isCaps){_hexCaps = isCaps;viewport()->update();}
}bool QHexEdit::hexCaps()
{return _hexCaps;
}void QHexEdit::setDynamicBytesPerLine(const bool isDynamic)
{_dynamicBytesPerLine = isDynamic;resizeEvent(NULL);
}bool QHexEdit::dynamicBytesPerLine()
{return _dynamicBytesPerLine;
}// ********************************************************************** Access to data of qhexedit
bool QHexEdit::setData(QIODevice &iODevice)
{bool ok = _chunks->setIODevice(iODevice);init();dataChangedPrivate();return ok;
}QByteArray QHexEdit::dataAt(qint64 pos, qint64 count)
{return _chunks->data(pos, count);
}bool QHexEdit::write(QIODevice &iODevice, qint64 pos, qint64 count)
{return _chunks->write(iODevice, pos, count);
}// ********************************************************************** Char handling
void QHexEdit::insert(qint64 index, char ch)
{_undoStack->insert(index, ch);refresh();
}void QHexEdit::remove(qint64 index, qint64 len)
{_undoStack->removeAt(index, len);refresh();
}void QHexEdit::replace(qint64 index, char ch)
{_undoStack->overwrite(index, ch);refresh();
}// ********************************************************************** ByteArray handling
void QHexEdit::insert(qint64 pos, const QByteArray &ba)
{_undoStack->insert(pos, ba);refresh();
}void QHexEdit::replace(qint64 pos, qint64 len, const QByteArray &ba)
{_undoStack->overwrite(pos, len, ba);refresh();
}// ********************************************************************** Utility functions
void QHexEdit::ensureVisible()
{if (_cursorPosition < (_bPosFirst * 2))verticalScrollBar()->setValue((int)(_cursorPosition / 2 / _bytesPerLine));if (_cursorPosition > ((_bPosFirst + (_rowsShown - 1)*_bytesPerLine) * 2))verticalScrollBar()->setValue((int)(_cursorPosition / 2 / _bytesPerLine) - _rowsShown + 1);if (_pxCursorX < horizontalScrollBar()->value())horizontalScrollBar()->setValue(_pxCursorX);if ((_pxCursorX + _pxCharWidth) > (horizontalScrollBar()->value() + viewport()->width()))horizontalScrollBar()->setValue(_pxCursorX + _pxCharWidth - viewport()->width());viewport()->update();
}qint64 QHexEdit::indexOf(const QByteArray &ba, qint64 from)
{qint64 pos = _chunks->indexOf(ba, from);if (pos > -1){qint64 curPos = pos*2;setCursorPosition(curPos + ba.length()*2);resetSelection(curPos);setSelection(curPos + ba.length()*2);ensureVisible();}return pos;
}bool QHexEdit::isModified()
{return _modified;
}qint64 QHexEdit::lastIndexOf(const QByteArray &ba, qint64 from)
{qint64 pos = _chunks->lastIndexOf(ba, from);if (pos > -1){qint64 curPos = pos*2;setCursorPosition(curPos - 1);resetSelection(curPos);setSelection(curPos + ba.length()*2);ensureVisible();}return pos;
}void QHexEdit::redo()
{_undoStack->redo();setCursorPosition(_chunks->pos()*(_editAreaIsAscii ? 1 : 2));refresh();
}QString QHexEdit::selectionToReadableString()
{QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());return toReadable(ba);
}QString QHexEdit::selectedData()
{QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex();return ba;
}void QHexEdit::setFont(const QFont &font)
{QFont theFont(font);theFont.setStyleHint(QFont::Monospace);QWidget::setFont(theFont);QFontMetrics metrics = fontMetrics();#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0)_pxCharWidth = metrics.horizontalAdvance(QLatin1Char('2'));
#else_pxCharWidth = metrics.width(QLatin1Char('2'));
#endif_pxCharHeight   = metrics.height();_pxGapAdr       = _pxCharWidth / 2;_pxGapAdrHex    = _pxCharWidth;_pxGapHexAscii  = 2 * _pxCharWidth;_pxCursorWidth  = _pxCharHeight / 7;_pxSelectionSub = _pxCharHeight / 5;viewport()->update();
}QString QHexEdit::toReadableString()
{QByteArray ba = _chunks->data();return toReadable(ba);
}void QHexEdit::undo()
{_undoStack->undo();setCursorPosition(_chunks->pos()*(_editAreaIsAscii ? 1 : 2));refresh();
}// ********************************************************************** Handle events
void QHexEdit::keyPressEvent(QKeyEvent *event)
{// Cursor movementsif (event->matches(QKeySequence::MoveToNextChar)){qint64 pos = _cursorPosition + 1;if (_editAreaIsAscii)pos += 1;setCursorPosition(pos);resetSelection(pos);}if (event->matches(QKeySequence::MoveToPreviousChar)){qint64 pos = _cursorPosition - 1;if (_editAreaIsAscii)pos -= 1;setCursorPosition(pos);resetSelection(pos);}if (event->matches(QKeySequence::MoveToEndOfLine)){qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1;setCursorPosition(pos);resetSelection(_cursorPosition);}if (event->matches(QKeySequence::MoveToStartOfLine)){qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine));setCursorPosition(pos);resetSelection(_cursorPosition);}if (event->matches(QKeySequence::MoveToPreviousLine)){setCursorPosition(_cursorPosition - (2 * _bytesPerLine));resetSelection(_cursorPosition);}if (event->matches(QKeySequence::MoveToNextLine)){setCursorPosition(_cursorPosition + (2 * _bytesPerLine));resetSelection(_cursorPosition);}if (event->matches(QKeySequence::MoveToNextPage)){setCursorPosition(_cursorPosition + (((_rowsShown - 1) * 2 * _bytesPerLine)));resetSelection(_cursorPosition);}if (event->matches(QKeySequence::MoveToPreviousPage)){setCursorPosition(_cursorPosition - (((_rowsShown - 1) * 2 * _bytesPerLine)));resetSelection(_cursorPosition);}if (event->matches(QKeySequence::MoveToEndOfDocument)){setCursorPosition(_chunks->size() * 2 );resetSelection(_cursorPosition);}if (event->matches(QKeySequence::MoveToStartOfDocument)){setCursorPosition(0);resetSelection(_cursorPosition);}// Select commandsif (event->matches(QKeySequence::SelectAll)){resetSelection(0);setSelection(2 * _chunks->size() + 1);}if (event->matches(QKeySequence::SelectNextChar)){qint64 pos = _cursorPosition + 1;if (_editAreaIsAscii)pos += 1;setCursorPosition(pos);setSelection(pos);}if (event->matches(QKeySequence::SelectPreviousChar)){qint64 pos = _cursorPosition - 1;if (_editAreaIsAscii)pos -= 1;setSelection(pos);setCursorPosition(pos);}if (event->matches(QKeySequence::SelectEndOfLine)){qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine)) + (2 * _bytesPerLine) - 1;setCursorPosition(pos);setSelection(pos);}if (event->matches(QKeySequence::SelectStartOfLine)){qint64 pos = _cursorPosition - (_cursorPosition % (2 * _bytesPerLine));setCursorPosition(pos);setSelection(pos);}if (event->matches(QKeySequence::SelectPreviousLine)){qint64 pos = _cursorPosition - (2 * _bytesPerLine);setCursorPosition(pos);setSelection(pos);}if (event->matches(QKeySequence::SelectNextLine)){qint64 pos = _cursorPosition + (2 * _bytesPerLine);setCursorPosition(pos);setSelection(pos);}if (event->matches(QKeySequence::SelectNextPage)){qint64 pos = _cursorPosition + (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine);setCursorPosition(pos);setSelection(pos);}if (event->matches(QKeySequence::SelectPreviousPage)){qint64 pos = _cursorPosition - (((viewport()->height() / _pxCharHeight) - 1) * 2 * _bytesPerLine);setCursorPosition(pos);setSelection(pos);}if (event->matches(QKeySequence::SelectEndOfDocument)){qint64 pos = _chunks->size() * 2;setCursorPosition(pos);setSelection(pos);}if (event->matches(QKeySequence::SelectStartOfDocument)){qint64 pos = 0;setCursorPosition(pos);setSelection(pos);}// Edit Commandsif (!_readOnly){/* Cut */if (event->matches(QKeySequence::Cut)){QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex();for (qint64 idx = 32; idx < ba.size(); idx +=33)ba.insert(idx, "\n");QClipboard *clipboard = QApplication::clipboard();clipboard->setText(ba);if (_overwriteMode){qint64 len = getSelectionEnd() - getSelectionBegin();replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0)));}else{remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());}setCursorPosition(2 * getSelectionBegin());resetSelection(2 * getSelectionBegin());} else/* Paste */if (event->matches(QKeySequence::Paste)){QClipboard *clipboard = QApplication::clipboard();QByteArray ba = QByteArray().fromHex(clipboard->text().toLatin1());if (_overwriteMode){ba = ba.left(std::min<qint64>(ba.size(), (_chunks->size() - _bPosCurrent)));replace(_bPosCurrent, ba.size(), ba);}elseinsert(_bPosCurrent, ba);setCursorPosition(_cursorPosition + 2 * ba.size());resetSelection(getSelectionBegin());} else/* Delete char */if (event->matches(QKeySequence::Delete)){if (getSelectionBegin() != getSelectionEnd()){_bPosCurrent = getSelectionBegin();if (_overwriteMode){QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0));replace(_bPosCurrent, ba.size(), ba);}else{remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin());}}else{if (_overwriteMode)replace(_bPosCurrent, char(0));elseremove(_bPosCurrent, 1);}setCursorPosition(2 * _bPosCurrent);resetSelection(2 * _bPosCurrent);} else/* Backspace */if ((event->key() == Qt::Key_Backspace) && (event->modifiers() == Qt::NoModifier)){if (getSelectionBegin() != getSelectionEnd()){_bPosCurrent = getSelectionBegin();setCursorPosition(2 * _bPosCurrent);if (_overwriteMode){QByteArray ba = QByteArray(getSelectionEnd() - getSelectionBegin(), char(0));replace(_bPosCurrent, ba.size(), ba);}else{remove(_bPosCurrent, getSelectionEnd() - getSelectionBegin());}resetSelection(2 * _bPosCurrent);}else{bool behindLastByte = false;if ((_cursorPosition / 2) == _chunks->size())behindLastByte = true;_bPosCurrent -= 1;if (_overwriteMode)replace(_bPosCurrent, char(0));elseremove(_bPosCurrent, 1);if (!behindLastByte)_bPosCurrent -= 1;setCursorPosition(2 * _bPosCurrent);resetSelection(2 * _bPosCurrent);}} else/* undo */if (event->matches(QKeySequence::Undo)){undo();} else/* redo */if (event->matches(QKeySequence::Redo)){redo();} elseif ((QApplication::keyboardModifiers() == Qt::NoModifier) ||(QApplication::keyboardModifiers() == Qt::KeypadModifier) ||(QApplication::keyboardModifiers() == Qt::ShiftModifier) ||(QApplication::keyboardModifiers() == (Qt::AltModifier | Qt::ControlModifier)) ||(QApplication::keyboardModifiers() == Qt::GroupSwitchModifier)){/* Hex and ascii input */int key;if (_editAreaIsAscii)key = (uchar)event->text()[0].toLatin1();elsekey = int(event->text()[0].toLower().toLatin1());if ((((key >= '0' && key <= '9') || (key >= 'a' && key <= 'f')) && _editAreaIsAscii == false)|| (key >= ' ' && _editAreaIsAscii)){if (getSelectionBegin() != getSelectionEnd()){if (_overwriteMode){qint64 len = getSelectionEnd() - getSelectionBegin();replace(getSelectionBegin(), (int)len, QByteArray((int)len, char(0)));} else{remove(getSelectionBegin(), getSelectionEnd() - getSelectionBegin());_bPosCurrent = getSelectionBegin();}setCursorPosition(2 * _bPosCurrent);resetSelection(2 * _bPosCurrent);}// If insert mode, then insert a byteif (_overwriteMode == false)if ((_cursorPosition % 2) == 0)insert(_bPosCurrent, char(0));// Change contentif (_chunks->size() > 0){char ch = key;if (!_editAreaIsAscii){QByteArray hexValue = _chunks->data(_bPosCurrent, 1).toHex();if ((_cursorPosition % 2) == 0)hexValue[0] = key;elsehexValue[1] = key;ch = QByteArray().fromHex(hexValue)[0];}replace(_bPosCurrent, ch);if (_editAreaIsAscii)setCursorPosition(_cursorPosition + 2);elsesetCursorPosition(_cursorPosition + 1);resetSelection(_cursorPosition);}}}}/* Copy */if (event->matches(QKeySequence::Copy)){QByteArray ba = _chunks->data(getSelectionBegin(), getSelectionEnd() - getSelectionBegin()).toHex();for (qint64 idx = 32; idx < ba.size(); idx +=33)ba.insert(idx, "\n");QClipboard *clipboard = QApplication::clipboard();clipboard->setText(ba);}// Switch between insert/overwrite modeif ((event->key() == Qt::Key_Insert) && (event->modifiers() == Qt::NoModifier)){setOverwriteMode(!overwriteMode());setCursorPosition(_cursorPosition);}// switch from hex to ascii editif (event->key() == Qt::Key_Tab && !_editAreaIsAscii){_editAreaIsAscii = true;setCursorPosition(_cursorPosition);}// switch from ascii to hex editif (event->key() == Qt::Key_Backtab  && _editAreaIsAscii){_editAreaIsAscii = false;setCursorPosition(_cursorPosition);}refresh();QAbstractScrollArea::keyPressEvent(event);
}void QHexEdit::mouseMoveEvent(QMouseEvent * event)
{_blink = false;viewport()->update();qint64 actPos = cursorPosition(event->pos());if (actPos >= 0){setCursorPosition(actPos);setSelection(actPos);}
}void QHexEdit::mousePressEvent(QMouseEvent * event)
{_blink = false;viewport()->update();qint64 cPos = cursorPosition(event->pos());if (cPos >= 0){if (event->button() != Qt::RightButton)resetSelection(cPos);setCursorPosition(cPos);}
}void QHexEdit::paintEvent(QPaintEvent *event)
{QPainter painter(viewport());auto oldPen = painter.pen();oldPen.setColor(_dataAreaPenColor);painter.setPen(oldPen);int pxOfsX = horizontalScrollBar()->value();if (event->rect() != _cursorRect){int pxPosStartY = _pxCharHeight;// draw some patterns if neededpainter.fillRect(event->rect(), viewport()->palette().color(QPalette::Base));if (_addressArea)painter.fillRect(QRect(-pxOfsX, event->rect().top(), _pxPosHexX - _pxGapAdrHex/2, height()), _addressAreaColor);/*if (_asciiArea){int linePos = _pxPosAsciiX - (_pxGapHexAscii / 2);painter.setPen(Qt::gray);painter.drawLine(linePos - pxOfsX, event->rect().top(), linePos - pxOfsX, height());}*/painter.setPen(viewport()->palette().color(QPalette::WindowText));// paint address areaif (_addressArea){QString address;oldPen.setColor(_addressAreaPenColor);painter.setPen(oldPen);for (int row=0, pxPosY = _pxCharHeight*2; row <= (_dataShown.size()/_bytesPerLine); row++, pxPosY +=_pxCharHeight){address = QString("%1").arg(_bPosFirst + row*_bytesPerLine + _addressOffset, _addrDigits, 16, QChar('0'));painter.drawText(_pxPosAdrX - pxOfsX, pxPosY, hexCaps() ? address.toUpper() : address);}}// paint hex and ascii areaoldPen.setColor(_dataAreaPenColor);painter.setPen(oldPen);QPen colStandard = QPen(viewport()->palette().color(QPalette::WindowText));painter.setBackgroundMode(Qt::TransparentMode);int pxPosX1 = _pxPosHexX  - pxOfsX;for (int i = 0, pxPosY = pxPosStartY; i <= 15; i++){QString address;address = QString("%1").arg(i,2,16,QLatin1Char('0'));painter.drawText(pxPosX1, pxPosY, address.toUpper());pxPosX1 += 3*_pxCharWidth;}for (int row = 0, pxPosY = pxPosStartY*2; row <= _rowsShown; row++, pxPosY +=_pxCharHeight){QByteArray hex;int pxPosX = _pxPosHexX  - pxOfsX;int pxPosAsciiX2 = _pxPosAsciiX  - pxOfsX;qint64 bPosLine = row * _bytesPerLine;for (int colIdx = 0; ((bPosLine + colIdx) < _dataShown.size() && (colIdx < _bytesPerLine)); colIdx++){QColor c = viewport()->palette().color(QPalette::Base);painter.setPen(colStandard);qint64 posBa = _bPosFirst + bPosLine + colIdx;if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa)){c = _brushSelection.color();painter.setPen(_penSelection);}else{if (_highlighting)if (_markedShown.at((int)(posBa - _bPosFirst))){c = _brushHighlighted.color();painter.setPen(_penHighlighted);}}// render hex valueQRect r;if (colIdx == 0)r.setRect(pxPosX, pxPosY - _pxCharHeight + _pxSelectionSub, 2*_pxCharWidth, _pxCharHeight);elser.setRect(pxPosX - _pxCharWidth, pxPosY - _pxCharHeight + _pxSelectionSub, 3*_pxCharWidth, _pxCharHeight);painter.fillRect(r, c);hex = _hexDataShown.mid((bPosLine + colIdx) * 2, 2);painter.drawText(pxPosX, pxPosY, hexCaps()?hex.toUpper():hex);pxPosX += 3*_pxCharWidth;// render ascii valueif (_asciiArea){int ch = (uchar)_dataShown.at(bPosLine + colIdx);if ( ch < ' ' || ch > '~' )ch = '.';r.setRect(pxPosAsciiX2, pxPosY - _pxCharHeight + _pxSelectionSub, _pxCharWidth, _pxCharHeight);painter.fillRect(r, c);painter.drawText(pxPosAsciiX2, pxPosY, QChar(ch));pxPosAsciiX2 += _pxCharWidth;}}}painter.setBackgroundMode(Qt::TransparentMode);painter.setPen(viewport()->palette().color(QPalette::WindowText));}// _cursorPosition counts in 2, _bPosFirst counts in 1int hexPositionInShowData = _cursorPosition - 2 * _bPosFirst;// due to scrolling the cursor can go out of the currently displayed dataif ((hexPositionInShowData >= 0) && (hexPositionInShowData < _hexDataShown.size())){// paint cursorif (_readOnly){// make the background stick outQColor color = viewport()->palette().dark().color();painter.fillRect(QRect(_pxCursorX - pxOfsX, _pxCursorY - _pxCharHeight + _pxSelectionSub, _pxCharWidth, _pxCharHeight), color);}else{if (_blink && hasFocus())painter.fillRect(_cursorRect, this->palette().color(QPalette::WindowText));}if (_editAreaIsAscii){// every 2 hex there is 1 asciiint asciiPositionInShowData = hexPositionInShowData / 2;if ((asciiPositionInShowData-16)>=0){int ch = (uchar)_dataShown.at(asciiPositionInShowData-16);if (ch < ' ' || ch > '~')ch = '.';painter.drawText(_pxCursorX - pxOfsX, _pxCursorY, QChar(ch));}elsepainter.drawText(_pxCursorX - pxOfsX, _pxCursorY, QChar(' '));}else{if ((hexPositionInShowData-32) >= 0)painter.drawText(_pxCursorX - pxOfsX, _pxCursorY, hexCaps() ? _hexDataShown.mid(hexPositionInShowData-32, 1).toUpper() : _hexDataShown.mid(hexPositionInShowData-32, 1));else{QString address;address = QString("%1").arg(hexPositionInShowData%2 == 0 ? 0 : hexPositionInShowData/2,1,16,QLatin1Char('0'));painter.drawText(_pxCursorX - pxOfsX, _pxCursorY, address.toUpper());}}}// emit event, if size has changedif (_lastEventSize != _chunks->size()){_lastEventSize = _chunks->size();emit currentSizeChanged(_lastEventSize);}
}void QHexEdit::resizeEvent(QResizeEvent *)
{if (_dynamicBytesPerLine){int pxFixGaps = 0;if (_addressArea)pxFixGaps = addressWidth() * _pxCharWidth + _pxGapAdr;pxFixGaps += _pxGapAdrHex;if (_asciiArea)pxFixGaps += _pxGapHexAscii;// +1 because the last hex value do not have space. so it is effective one char moreint charWidth = (viewport()->width() - pxFixGaps ) / _pxCharWidth + 1;// 2 hex alfa-digits 1 space 1 ascii per byte = 4; if ascii is disabled then 3// to prevent devision by zero use the min value 1setBytesPerLine(std::max(charWidth / (_asciiArea ? 4 : 3),1));}adjust();
}bool QHexEdit::focusNextPrevChild(bool next)
{if (_addressArea){if ( (next && _editAreaIsAscii) || (!next && !_editAreaIsAscii ))return QWidget::focusNextPrevChild(next);elsereturn false;}else{return QWidget::focusNextPrevChild(next);}
}// ********************************************************************** Handle selections
void QHexEdit::resetSelection()
{_bSelectionBegin = _bSelectionInit;_bSelectionEnd = _bSelectionInit;
}void QHexEdit::resetSelection(qint64 pos)
{pos = pos / 2 ;if (pos < 0)pos = 0;if (pos > _chunks->size())pos = _chunks->size();_bSelectionInit = pos;_bSelectionBegin = pos;_bSelectionEnd = pos;
}void QHexEdit::setSelection(qint64 pos)
{pos = pos / 2;if (pos < 0)pos = 0;if (pos > _chunks->size())pos = _chunks->size();if (pos >= _bSelectionInit){_bSelectionEnd = pos;_bSelectionBegin = _bSelectionInit;}else{_bSelectionBegin = pos;_bSelectionEnd = _bSelectionInit;}
}qint64 QHexEdit::getSelectionBegin()
{return _bSelectionBegin-16;
}qint64 QHexEdit::getSelectionEnd()
{return _bSelectionEnd-16;
}// ********************************************************************** Private utility functions
void QHexEdit::init()
{_undoStack->clear();setAddressOffset(0);resetSelection(0);setCursorPosition(0);verticalScrollBar()->setValue(0);_modified = false;
}void QHexEdit::adjust()
{// recalc Graphicsif (_addressArea){_addrDigits = addressWidth();_pxPosHexX = _pxGapAdr + _addrDigits*_pxCharWidth + _pxGapAdrHex;}else_pxPosHexX = _pxGapAdrHex;_pxPosAdrX = _pxGapAdr;_pxPosAsciiX = _pxPosHexX + _hexCharsInLine * _pxCharWidth + _pxGapHexAscii;// set horizontalScrollBar()int pxWidth = _pxPosAsciiX;if (_asciiArea)pxWidth += _bytesPerLine*_pxCharWidth;horizontalScrollBar()->setRange(0, pxWidth - viewport()->width());horizontalScrollBar()->setPageStep(viewport()->width());// set verticalScrollbar()_rowsShown = ((viewport()->height()-4)/_pxCharHeight);if((viewport()->height()-4)%_pxCharHeight > 0)++_rowsShown;int lineCount = (int)(_chunks->size() / (qint64)_bytesPerLine) + 3;verticalScrollBar()->setRange(0, lineCount - _rowsShown);verticalScrollBar()->setPageStep(_rowsShown);int value = verticalScrollBar()->value();_bPosFirst = (qint64)value * _bytesPerLine;_bPosLast = _bPosFirst + (qint64)(_rowsShown * _bytesPerLine) - 1;if (_bPosLast >= _chunks->size())_bPosLast = _chunks->size() - 1;readBuffers();setCursorPosition(_cursorPosition);
}void QHexEdit::dataChangedPrivate(int)
{_modified = _undoStack->index() != 0;adjust();emit dataChanged();
}void QHexEdit::refresh()
{ensureVisible();readBuffers();
}void QHexEdit::readBuffers()
{_dataShown = _chunks->data(_bPosFirst, _bPosLast - _bPosFirst + _bytesPerLine + 1, &_markedShown);_hexDataShown = QByteArray(_dataShown.toHex());
}QString QHexEdit::toReadable(const QByteArray &ba)
{QString result;for (int i=0; i < ba.size(); i += 16){QString addrStr = QString("%1").arg(_addressOffset + i, addressWidth(), 16, QChar('0'));QString hexStr;QString ascStr;for (int j=0; j<16; j++){if ((i + j) < ba.size()){hexStr.append(" ").append(ba.mid(i+j, 1).toHex());char ch = ba[i + j];if ((ch < 0x20) || (ch > 0x7e))ch = '.';ascStr.append(QChar(ch));}}result += addrStr + " " + QString("%1").arg(hexStr, -48) + "  " + QString("%1").arg(ascStr, -17) + "\n";}return result;
}void QHexEdit::updateCursor()
{if (_blink)_blink = false;else_blink = true;viewport()->update(_cursorRect);
}

相关文章:

Qt中显示hex数据的控件

效果类似QTextEdit&#xff0c;但是显示十六进制的数据&#xff0c;比如用于显示抓取串口或者bin文件的数据等等 chunks.h #ifndef CHUNKS_H #define CHUNKS_H/** \cond docNever *//*! The Chunks class is the storage backend for QHexEdit.** When QHexEdit loads data, C…...

python web 开发 - 常用Web框架

python web 开发 - 文章目录 python web 开发 -1、关于Web开发2、常用Web框架3、开发案例3.1. 使用Flask框架创建一个简单的Web应用程序3.2. 使用tornado框架创建一个简单的Web应用程序3.3. 使用Django框架创建一个简单的待办事项应用程序 4、总结 1、关于Web开发 Web当然是网…...

设计模式——适配器模式06

适配器模式 通常在已有程序中使用&#xff0c;至少有两个类的接口不兼容时&#xff0c;让相互不兼容的类能很好地合作。例如之前系统 用的Target 接口调用&#xff0c;现在要新增第三方接口Adaptee。如何进行适配&#xff0c;面向客户端提供 调用Target接口方式达到使用Adaptee…...

【鸿蒙开发】组件状态管理@Prop,@Link,@Provide,@Consume,@Observed,@ObjectLink

1. Prop 父子单向同步 概述 Prop装饰的变量和父组件建立单向的同步关系&#xff1a; Prop变量允许在本地修改&#xff0c;但修改后的变化不会同步回父组件。当父组件中的数据源更改时&#xff0c;与之相关的Prop装饰的变量都会自动更新。如果子组件已经在本地修改了Prop装饰…...

Web 前端性能优化之八:前端性能检测实践

五、前端性能检测实践 1、常用的检测工具 Lighthouse、Chrome开发者工具中与性能检测相关的一些工具面板、页面加载性能分析工具PageSpeed Insights、专业的性能检测工具WEBPAGETEST等 1、Chrome 任务管理器 通过Chrome任务管理器我们可以查看当前Chrome浏览器中&#xff0…...

安装VMware ESXi虚拟机系统

简介&#xff1a;ESXi是VMware公司开发的一款服务器虚拟化操作系统。它能够在一台物理服务器上运行多个虚拟机&#xff0c;每个虚拟机都可以独立运行操作系统和应用程序&#xff0c;而且对硬件配置要求低&#xff0c;系统运行稳定。 准备工具&#xff1a; 1.8G或者8G以上容…...

Vue3实践之全局请求URL配置和请求参数说明

Vue3实践之全局请求URL配置和请求参数说明 全局请求URL配置 1、首先需要导入router和axios import router from "/router";2、创建app&#xff0c;将router挂载到app上 const app createApp(App) app.use(router).use(elementIcons).mount(#app)3、设置全局请求…...

类和对象—初阶

目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 4.类的访问限定符及封装 4.1 访问限定符 【面试题】 4.2 封装 【面试题】 5.类的作用域 6.类的实例化 7.类对象模型 7.1 如何计算类对象的大小 7.2 类对象的存储方式 7.3 结构体内存对齐规则 【面试题】…...

【Linux】shell 脚本基础使用

在终端中输入命令可以完成一些常用的操作&#xff0c;但是我们都是一条一条输入命令&#xff0c;比较麻烦&#xff0c;为了解决这个问题&#xff0c;就会涉及到 shell 脚本&#xff0c;它可以将很多条命令放到一个文件里面&#xff0c;然后直接运行这个文件即可。 shell 脚本类…...

nfs部署--相关记录

以下是在 CentOS 8 中将 10.40.111.41 上的 /nfsdata 目录通过 NFS 共享到 10.40.111.43 和 10.40.111.45 的 /nfsdata 目录的详细步骤&#xff1a; 在 10.40.111.41 上操作&#xff1a; 安装并配置 NFS 服务器&#xff1a; a. 安装 NFS 服务器软件包&#xff1a; sudo dnf in…...

java国产化云HIS基层医院系统源码 SaaS模式

目录 ​ 云HIS开发环境 功能模块介绍&#xff1a; 1、门诊模块 2、住院模块 3、药房、药库模块 ​编辑 4、电子病历模块 5、统计报表模块 6、系统管理模块 系统优势 云his之电子病历子系统功能 云 his 系统是运用云计算、大数据、物联网等新兴信息技术&#xff0c;按…...

docker 部署 Epusdt - 独角数卡 dujiaoka 的 usdt 支付插件

部署 部署说明 部署之前必须注意的几点事项,该教程不一定适合所有用户: 本教程主要是使用 docker 部署,宝塔用户或宿主机直接安装的用户请直接参考官网教程.本教程是独立部署 epusdt,使用独立的mysql和redis,与dujiaoka项目分开. 在研究的过程中发现 epusdt 也需要用到 mys…...

数据结构排序之冒泡、快速、插入、选择、堆、归并等排序及时间,空间复杂度等(超详解,绝对能满足你的需求,并能学到很多有用知识)

在本文章开始之前给大家介绍个网站,可以通过下面动画网址来理解 ,(国外的网站帮助学习数据结构很多知识,可以翻译下来,在搜索框搜索相应的排序算法进行动画演示,非常好用。)​​​https://www.cs.usfca.edu/~galles/visualization/https://www.cs.usfca.edu/~galles/vis…...

如何在windows环境和linux环境运行jar包

功能:实现对字符串小写转大写 java代码如下: package a_od_test;import java.util.Locale; /* 实现小写转大写 打jar包 分别在windows环境和liunx环境运行*/ public class Main28_To_Upper {public static void main(String[] args) {if (args.length 1) {System.out.printl…...

2602B吉时利2602B数字源表

181/2461/8938产品概述&#xff1a; 2600B系列系统源表SMU仪器是业界领先的电流/电压源和测量解决方案&#xff0c;采用吉时利第三代SMU技术制造。2600B系列提供单通道和双通道型号&#xff0c;将精密电源、真电流源、6-1/2位数字多用表、任意波形发生器、脉冲发生器和电子负载…...

Linux——fork复制进程

1)shell: 在计算机科学中&#xff0c;Shell俗称壳&#xff08;用来区别于核&#xff09;&#xff0c;是指“为使用者提供操作界面”的软件&#xff08;command interpreter&#xff0c;命令解析器&#xff09;。它类似于DOS下的COMMAND.COM和后来的cmd.exe。它接收用户命令&…...

李廉洋:4.10黄金原油晚间走势最新分析及策略。

鉴于美联储官员对1月和2月通胀数据反应不足&#xff0c;3月通胀数据过热可能导致其反应过度的风险更大。美联储试图避免根据一两个数据点来制定政策&#xff0c;但今年迄今为止经济活动的韧性意味着&#xff0c;在年中降息的理由取决于通胀是否恢复自去年下半年以来的稳步下降趋…...

【头歌-Python】字符串自学引导

禁止转载&#xff0c;原文&#xff1a;https://blog.csdn.net/qq_45801887/article/details/137517279 参考教程&#xff1a;B站视频讲解——https://space.bilibili.com/3546616042621301 如果代码存在问题&#xff0c;麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 字符串自学引导 …...

44-技术演进(下):软件架构和应用生命周期技术演进之路

应用、系统资源、应用生命周期管理这 3 个维度&#xff0c;构成了我们对云的所有诉求。 我会介绍下应用维度和应用生命周期管理维度的技术演进。 我们就先来看下软件架构的演进之路。 软件架构的演进 软件架构技术演进如下图所示&#xff1a; 单体架构 在单体架构中&#xff…...

【C++】C++中的list

一、介绍 官方给的 list的文档介绍 简单来说就是&#xff1a; list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

华为OD机试-最短木板长度-二分法(A卷,100分)

此题是一个最大化最小值的典型例题&#xff0c; 因为搜索范围是有界的&#xff0c;上界最大木板长度补充的全部木料长度&#xff0c;下界最小木板长度&#xff1b; 即left0,right10^6; 我们可以设置一个候选值x(mid)&#xff0c;将木板的长度全部都补充到x&#xff0c;如果成功…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统

Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...

STM32 低功耗设计全攻略:PWR 模块原理 + 睡眠 / 停止 / 待机模式实战(串口 + 红外 + RTC 应用全解析)

文章目录 PWRPWR&#xff08;电源控制模块&#xff09;核心功能 电源框图上电复位和掉电复位可编程电压监测器低功耗模式模式选择睡眠模式停止模式待机模式 修改主频一、准备工作二、修改主频的核心步骤&#xff1a;宏定义配置三、程序流程&#xff1a;时钟配置函数解析四、注意…...