QSpinBox with Unsigned Int for Hex Input - c++

There are a variety of questions written here about QSpinBox's limitation of using an int as its datatype. Often people want to display larger numbers. In my case, I want to be able to show an unsigned 32bit integer in hexadecimal. This means I'd like my range to be [0x0, 0xFFFFFFFF]. The largest a normal QSpinBox can go is 0x7FFFFFFF. Answering my own question here, the solution I came up with is to simply force the int to be treated like an unsigned int, by reimplementing the relevant display and validation functions.

The result is pretty simple, and it works well. Sharing here in case anyone else can benefit from this. It has a 32bit mode and a 16bit mode.
class HexSpinBox : public QSpinBox
{
public:
HexSpinBox(bool only16Bits, QWidget *parent = 0) : QSpinBox(parent), m_only16Bits(only16Bits)
{
setPrefix("0x");
setDisplayIntegerBase(16);
if (only16Bits)
setRange(0, 0xFFFF);
else
setRange(INT_MIN, INT_MAX);
}
unsigned int hexValue() const
{
return u(value());
}
void setHexValue(unsigned int value)
{
setValue(i(value));
}
protected:
QString textFromValue(int value) const
{
return QString::number(u(value), 16).toUpper();
}
int valueFromText(const QString &text) const
{
return i(text.toUInt(0, 16));
}
QValidator::State validate(QString &input, int &pos) const
{
QString copy(input);
if (copy.startsWith("0x"))
copy.remove(0, 2);
pos -= copy.size() - copy.trimmed().size();
copy = copy.trimmed();
if (copy.isEmpty())
return QValidator::Intermediate;
input = QString("0x") + copy.toUpper();
bool okay;
unsigned int val = copy.toUInt(&okay, 16);
if (!okay || (m_only16Bits && val > 0xFFFF))
return QValidator::Invalid;
return QValidator::Acceptable;
}
private:
bool m_only16Bits;
inline unsigned int u(int i) const
{
return *reinterpret_cast<unsigned int *>(&i);
}
inline int i(unsigned int u) const
{
return *reinterpret_cast<int *>(&u);
}
};

If you don't need full 32 bits you can do it very simply like this:
#pragma once
#include <QSpinBox>
class PaddedSpinBox : public QSpinBox
{
public:
PaddedSpinBox(QWidget *parent = 0) : QSpinBox(parent)
{
}
protected:
QString textFromValue(int value) const override
{
// Pad to the width of maximum().
int width = QString::number(maximum(), displayIntegerBase()).size();
return QString("%1").arg(value, width, displayIntegerBase(), QChar('0')).toUpper();
}
};
In the form designer (or whatever) then you just set:
prefix: 0x
displayIntegerBase: 16
maximum: 255 (or whatever)
If you need full 32 bits you will have to employ casting tricks, or maybe just use a line edit.

I came up with the same problem but using PyQt so I could not avoid the range checking that Qt was doing in C under the hood.
The workaround was to use a QDoulbeSpinbox and to cast the value to an int in textFromValue.
Here is my code (it also implements a right click menu to change the display base):
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from future_builtins import *
import re
import sys
from PyQt4.QtCore import (QRegExp, Qt)
from PyQt4.QtGui import (QApplication, QRegExpValidator, QDoubleSpinBox)
from PyQt4.QtCore import pyqtSlot,SIGNAL,SLOT
from PyQt4 import QtCore, QtGui
# Regex adapted from Mark Pilgrim's "Dive Into Python" book
class QHexSpinBox(QDoubleSpinBox):
def __init__(self, parent=None):
super(QHexSpinBox, self).__init__(parent)
self.mode = 'dec'
self.setContextMenuPolicy(Qt.CustomContextMenu);
regex = QRegExp("[x0-9A-Fa-f]{1,8}")
regex.setCaseSensitivity(Qt.CaseInsensitive)
self.hexvalidator = QRegExpValidator(regex, self)
regex = QRegExp("[0-9]{1,10}")
regex.setCaseSensitivity(Qt.CaseInsensitive)
self.decvalidator = QRegExpValidator(regex, self)
regex = QRegExp("[b0-1]{1,64}")
regex.setCaseSensitivity(Qt.CaseInsensitive)
self.binvalidator = QRegExpValidator(regex, self)
self.setRange(1, 999999)
self.connect(self,SIGNAL("customContextMenuRequested(QPoint)"),
self,SLOT("contextMenuRequested(QPoint)"))
#pyqtSlot(QtCore.QPoint)
def contextMenuRequested(self,point):
menu = QtGui.QMenu()
hex = menu.addAction("Hex")
dec = menu.addAction("Dec")
bin = menu.addAction("Bin")
self.connect(hex,SIGNAL("triggered()"),
self,SLOT("hex()"))
self.connect(dec,SIGNAL("triggered()"),
self,SLOT("dec()"))
self.connect(bin,SIGNAL("triggered()"),
self,SLOT("bin()"))
menu.exec_(self.mapToGlobal(point))
#pyqtSlot()
def hex(self):
self.mode = 'hex'
self.setValue(self.value())
#pyqtSlot()
def dec(self):
self.mode = 'dec'
self.setValue(self.value())
#pyqtSlot()
def bin(self):
self.mode = 'bin'
self.setValue(self.value())
def validate(self, text, pos):
if self.mode == 'hex':
return self.hexvalidator.validate(text, pos)
if self.mode == 'dec':
return self.decvalidator.validate(text, pos)
if self.mode == 'bin':
return self.binvalidator.validate(text, pos)
def valueFromText(self, text):
if self.mode == 'hex':
return int(unicode(text), 16)
elif self.mode == 'dec':
return int(unicode(text))
elif self.mode == 'bin':
return int(unicode(text), 2)
def textFromValue(self, value):
value = int(value)
if self.mode == 'hex':
return hex(value)
elif self.mode == 'dec':
return str(value)
elif self.mode =='bin':
return "0b{0:b}".format(value)

I know this is an old answer but came here from google. Here is my solution with pyside 1.2.4 based somewhat off of Techniquab's solution but doesn't have the integer overflow issue:
from PySide import QtCore, QtGui
from numpy import base_repr
from PySide.QtGui import QRegExpValidator
class QBaseSpinBox(QtGui.QAbstractSpinBox):
valueChanged = QtCore.Signal(int)
_value = 0
default_value = 0
base = 10
def __init__(self, parent=None):
self.setRange(None, None)
QtGui.QAbstractSpinBox.__init__(self, parent)
self.set_base(self.base)
self.lineEdit().setValidator(QRegExpValidator(self))
self.default_value = self.value()
self.lineEdit().textChanged.connect(self.textChanged)
self.lineEdit().setContextMenuPolicy(QtCore.Qt.CustomContextMenu);
self.lineEdit().customContextMenuRequested.connect(self.contextMenuRequested)
#QtCore.Slot()
def contextMenuRequested(self, point):
menu = self.lineEdit().createStandardContextMenu() #QtGui.QMenu()
actionDefault = menu.addAction("&Set Default Value of %s" % self.textFromValue(self.default_value),
shortcut=QtCore.Qt.CTRL | QtCore.Qt.Key_D) #QtGui.QKeySequence("Ctrl+D")))
menu.insertSeparator(actionDefault)
actionDefault.triggered.connect(self.menuActionDefault_triggered)
menu.exec_(self.mapToGlobal(point))
#QtCore.Slot()
def menuActionDefault_triggered(self):
self.setValue(self.default_value)
def value(self):
return self._value
def setValue(self, value):
if self.validate(value) == QtGui.QValidator.Invalid:
self.setValue(self._value)
return
changed = False
if self._value != value:
changed = True
self._value = value
self.lineEdit().setText(self.textFromValue(value))
if changed:
self.valueChanged.emit(self._value)
#QtCore.Slot()
def stepBy(self, value):
self.setValue(self._value + value)
QtGui.QAbstractSpinBox.stepBy(self, self._value)
def stepEnabled(self):
return QtGui.QAbstractSpinBox.StepDownEnabled | QtGui.QAbstractSpinBox.StepUpEnabled
#QtCore.Slot()
def textChanged(self, text):
try:
self.setValue(int(text, self.base))
except:
self.setValue(self._value)
def setRange(self, _min, _max):
self.minimum = _min if _min != None else 0
self.maximum = _max if _max != None else 0xFFFFFFFFFFFFFFFF
def validate(self, input):
if not input:
return QtGui.QValidator.Intermediate
try:
try:
value = int(input, self.base)
except TypeError:
value = input
if not (self.minimum <= input <= self.maximum):
raise Exception()
except Exception as ex:
return QtGui.QValidator.Invalid
return QtGui.QValidator.Acceptable
def valueFromText(self, text):
return int(text, self.base)
def textFromValue(self, value):
return base_repr(value, self.base).upper()
def set_default_value(self, value):
self.default_value = int(value)
#self.setValue(self.default_value)
self.set_base(self.base) # Redo the tooltip
def set_base(self, base):
self.base = base
min = self.textFromValue(self.minimum)
max = self.textFromValue(self.maximum)
default = self.textFromValue(self.default_value)
self.lineEdit().setToolTip("Base %d\nRange: %s-%s\nDefault Value: %s" % (self.base, min, max, default))

Thanks #ZX2C4 for the answer. I some modified the class HexSpinBox:
you may set prefix.
you may set max range (in case INT_MAX < maxRange < UINT_MAX there are bugs).
you may disable fill fields 0.
width of fields count auto.
hexspinbox.h
#ifndef HEXSPINBOX_H
#define HEXSPINBOX_H
#include <QSpinBox>
class HexSpinBox : public QSpinBox
{
Q_OBJECT
public:
HexSpinBox(QWidget *parent = nullptr);
unsigned int hexValue() const { return u(value()); }
void setHexValue(unsigned int value) { setValue(i(value)); }
void setRange(unsigned int max);
bool fillField() const { return m_fillField; }
void setFillField(bool fillFieldWidth) { m_fillField = fillFieldWidth; }
protected:
QString textFromValue(int value) const;
int valueFromText(const QString &text) const;
QValidator::State validate(QString &input, int &pos) const;
private:
unsigned int m_maxRange = UINT_MAX;
bool m_fillField = true;
inline unsigned int u(int i) const { return *reinterpret_cast<unsigned int *>(&i); }
inline int i(unsigned int u) const { return *reinterpret_cast<int *>(&u); }
};
#endif // HEXSPINBOX_H
hexspinbox.cpp
#include "hexspinbox.h"
HexSpinBox::HexSpinBox(QWidget *parent) : QSpinBox(parent), m_maxRange(maximum())
{
setDisplayIntegerBase(16);
}
void HexSpinBox::setRange(unsigned int max)
{
m_maxRange = max;
if (m_maxRange <= INT_MAX) {
QSpinBox::setRange(0, int(m_maxRange));
} else {
QSpinBox::setRange(INT_MIN, INT_MAX);
}
}
QString HexSpinBox::textFromValue(int value) const
{
int fillField = 0;
if (m_fillField) {
uint m = m_maxRange;
while (m) {
m >>= 4;
++fillField;
}
}
return QString("%1").arg(u(value), fillField, 16, QLatin1Char('0')).toUpper();
}
int HexSpinBox::valueFromText(const QString &text) const
{
return i(text.toUInt(nullptr, 16));
}
QValidator::State HexSpinBox::validate(QString &input, int &pos) const
{
QString copy(input);
QString pref = prefix();
if (copy.startsWith(pref))
copy.remove(pref);
pos -= copy.size() - copy.trimmed().size();
copy = copy.trimmed();
if (copy.isEmpty())
return QValidator::Intermediate;
input = pref + copy.toUpper();
bool okay;
unsigned int val = copy.toUInt(&okay, 16);
if (!okay || val > m_maxRange)
return QValidator::Invalid;
return QValidator::Acceptable;
}
You can use the class for the range [0x0, 0xFFFFFFFF] :
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
ui->hexspinbox->setRange(UINT_MAX); // or 0xFF =)
ui->hexspinbox->setPrefix("0x");
}

Related

Micropython User Module: Class Prints Initialized Data Even If Attributes Have Changed

Here's a visual representation of the problem from the front end. Notice that the output from printing the class does not change even if the attributes do, yet I get the proper value from printing the attributes directly.
import bitbanglab as bbl
class Entity(bbl.Entity):
def __init__(self):
super().__init__(bytearray([0x0F]), 1, 1, 2, 2, 8, 2)
print(self) #Entity(x:1, y:1, width:2, height:2, scale:8, len:1, pal:2)
self.x = 50
self.y = 20
self.width = 50
self.height= 60
self.scale = 10
self.len = 200
self.pal = 14
print(self) #Entity(x:1, y:1, width:2, height:2, scale:8, len:1, pal:2)
print(self.x) #50
print(self.y) #20
Entity()
Print Method
STATIC void Entity_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
bitbanglab_Entity_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "Entity(x:%u, y:%u, width:%u, height:%u, scale:%u, len:%u, pal:%u)", self->x, self->y, self->width, self->height, self->scale, self->len, self->pal);
}
get/set attributes
STATIC void Entity_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
bitbanglab_Entity_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (dest[0] == MP_OBJ_NULL) {
if (attr == MP_QSTR_x)
dest[0] = mp_obj_new_int(self->x);
else if(attr == MP_QSTR_y)
dest[0] = mp_obj_new_int(self->y);
else if(attr == MP_QSTR_width)
dest[0] = mp_obj_new_int(self->width);
else if(attr == MP_QSTR_height)
dest[0] = mp_obj_new_int(self->height);
else if(attr == MP_QSTR_scale)
dest[0] = mp_obj_new_int(self->scale);
else if(attr == MP_QSTR_len)
dest[0] = mp_obj_new_int(self->len);
else if(attr == MP_QSTR_pal)
dest[0] = mp_obj_new_int(self->pal);
} else {
if (attr == MP_QSTR_x)
self->x = mp_obj_get_int(dest[1]);
else if (attr == MP_QSTR_y)
self->y = mp_obj_get_int(dest[1]);
else if (attr == MP_QSTR_width)
self->width = mp_obj_get_int(dest[1]);
else if (attr == MP_QSTR_height)
self->height = mp_obj_get_int(dest[1]);
else if (attr == MP_QSTR_scale)
self->scale = mp_obj_get_int(dest[1]);
else if (attr == MP_QSTR_len)
self->len = mp_obj_get_int(dest[1]);
else if (attr == MP_QSTR_pal)
self->pal = mp_obj_get_int(dest[1]);
dest[0] = MP_OBJ_NULL;
}
}
Class Type
const mp_obj_type_t bitbanglab_Entity_type = {
{ &mp_type_type },
.name = MP_QSTR_bitbanglab,
.print = Entity_print,
.make_new = Entity_make_new,
.locals_dict = (mp_obj_dict_t*)&Entity_locals_dict,
.attr = Entity_attr,
};
Class Object
typedef struct _bitbanglab_Entity_obj_t {
mp_obj_base_t base;
uint8_t *bitmap;
uint16_t len;
uint16_t x;
uint16_t y;
uint16_t width;
uint16_t height;
uint8_t scale;
uint8_t pal;
} bitbanglab_Entity_obj_t;
Class
The class doesn't have any methods yet. The locals_dict_table for it is empty.
mp_obj_t Entity_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 7, 7, true);
//make self
bitbanglab_Entity_obj_t *self = m_new_obj(bitbanglab_Entity_obj_t);
self->base.type = &bitbanglab_Entity_type;
//arguments
enum { ARG_bitmap, ARG_x, ARG_y, ARG_width, ARG_height, ARG_scale, ARG_pal};
//get buffer
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_bitmap], &bufinfo, MP_BUFFER_RW);
//properties
self->bitmap = (uint8_t *)bufinfo.buf;
self->len = bufinfo.len;
self->x = mp_obj_get_int(args[ARG_x]);
self->y = mp_obj_get_int(args[ARG_y]);
self->width = mp_obj_get_int(args[ARG_width]);
self->height = mp_obj_get_int(args[ARG_height]);
self->scale = mp_obj_get_int(args[ARG_scale]);
self->pal = mp_obj_get_int(args[ARG_pal]);
return MP_OBJ_FROM_PTR(self);
}
I realized the problem. I was subclassing my user-module-defined class, and the values I was setting were not the ones on the super. If I instantiate the Entity class without a subclass and change the attributes, printing the class reflects those changes. Now my new problem is figuring out how to reach the super's attributes from a subclass. I'll update this answer to be stronger when I figure it all out.

Adding a custom type to a RedBlackTree

I want to keep an ordered set of records and the standard provides me with RedBlackTree. The record is of type Tuple!(string, uint). Here's what it looks like:
import std.json : parseJSON;
uint[string] wordTable;
import std.datetime.stopwatch : StopWatch, AutoStart;
auto sw = StopWatch(AutoStart.yes);
const auto j = parseJSON(get(link));
const long downloadTime = sw.peek.total!"msecs";
import std.typecons : Tuple, tuple;
import std.container.rbtree : RedBlackTree;
import std.functional : binaryFun;
RedBlackTree!(Tuple!(string, uint), binaryFun!("a[1] > b[1]")) records;
foreach (node; j["posts"].array()) {
import std.stdio : writeln;
import std.utf : decode;
if ("com" in node) {
import std.algorithm : splitter;
foreach (word; getStr(node["com"].str()).splitter(' ')) {
import std.string : strip;
if (word.strip().length > 0)
wordTable.require(word, 0)++;
records ~= (tuple(word, wordTable[word])); // error
}
}
}
Now primarily I had used insert() method to add a record to the records but it causes segfault in runtime. So I decided to use ~= in hopes for better error messages. And here's what the compiler says:
Error: cannot append type Tuple!(string, uint) to type std.container.rbtree.RedBlackTree!(Tuple!(string, uint), binaryFun, false)
According to https://dlang.org/phobos/std_container_rbtree.html#.RedBlackTree I have to provide a type such that calling less(a, b) on it returns a boolean. So I went ahead and created a type for it:
struct Record {
string key;
uint value;
int opCmp(ref const Record other) const {
return value - other.value;
}
}
// bool less(Record a, Record b) {
// return a < b;
// }
void main(string[] args) {
import std.stdio : writeln, writefln;
if (args.length < 3) {
writeln("Must have 2 arguments " ~ "first argument is the link, "
~ "the second one is for minimum repeatation threshold. Exiting.");
import core.stdc.stdlib : exit;
exit(-1);
}
const auto link = parseLink(args[1]);
const auto threshold = atoui(args[2]);
import std.json : parseJSON;
uint[string] wordTable;
import std.datetime.stopwatch : StopWatch, AutoStart;
auto sw = StopWatch(AutoStart.yes);
const auto j = parseJSON(get(link));
const long downloadTime = sw.peek.total!"msecs";
import std.container.rbtree : RedBlackTree;
import std.functional : binaryFun;
RedBlackTree!Record records;
foreach (node; j["posts"].array()) {
import std.utf : decode;
if ("com" in node) {
import std.algorithm : splitter;
foreach (word; getStr(node["com"].str()).splitter(' ')) {
import std.string : strip;
if (word.strip().length > 0)
wordTable.require(word, 0)++;
records ~= (Record(word, wordTable[word]));
}
}
}
This time the compiler complains:
Error: cannot append type Record to type std.container.rbtree.RedBlackTree!(Record, "a < b", false)
So the gist of the question is, if I have an RedBlackTree with a custom binaryFun, how can I add an instance of a tuple or a custom type to it?

Return of 2 identical userdata

I want new and at to return the same userdata, please tell me how to do it
example code that I use
struct SCheckpoint {
rage::ICheckpoint* r_Checkpoint;
SCheckpoint(rage::ICheckpoint* checkpoint) {
r_Checkpoint = checkpoint;
}
int GetId() const {
return r_Checkpoint->GetId();
}
}
int createCheckpoint(lua_State* state) {
rage::ICheckpoint* checkpoint = RaluCore::getInstance().getMultiPlayer()->GetCheckpointPool().New(model, vec, vec, radius, color, defaultShow, dimension);
SCheckpoint* scriptCheckpoint = new SCheckpoint(checkpoint);
checkpoint->External(scriptCheckpoint);
push(state, scriptCheckpoint);
return 1;
}
int atCheckpoint(lua_State* state) {
LuaRef ref = LuaRef::fromStack(state, 1);
rage::ICheckpoint* checkpoint = RaluCore::getInstance().getMultiPlayer()->GetCheckpointPool().GetAt(ref);
SCheckpoint* scriptCheckpoint = checkpoint->External<SCheckpoint>();
push (state, scriptCheckpoint);
return 1;
}
getGlobalNamespace(state)
.beginClass<SCheckpoint>("SCheckpoint")
.addProperty("id", &SCheckpoint::GetId)
.endClass()
.addCFunction("at", &atCheckpoint)
.addCFunction("new", &createCheckpoint);
userdata has different addresses, because of this new != at
local createdCheckpoint = mp.checkpoints.new()
print(createdCheckpoint.id)
local gettedCheckpoint = mp.checkpoints.at(0)
print(gettedCheckpoint.id)
print(createdCheckpoint, gettedCheckpoint, createdCheckpoint == gettedCheckpoint)
returned -
0
0
userdata: 0x34a8c320 userdata: 0x34a8d5b0 false

Convert a dicom tag to its string name in gdcm in C++

Given a gdcm tag, e.g. gdcm::Tag(0x0010,0x0010) how can it be convert to the corresponding tag name string, in this case "PatientsName" in C++?
Here is what I do in a Qt based application with GDCM:
QString duDicomDictionary::getTagName( const gdcm::Tag & tag )
{
QString retVal;
const gdcm::Global& g = gdcm::Global::GetInstance();
const gdcm::Dicts &dicts = g.GetDicts();
const gdcm::Dict &pubdict = dicts.GetPublicDict();
gdcm::DictEntry ent = pubdict.GetDictEntry(tag);
if (ent.GetVR() != gdcm::VR::INVALID ) {
retVal = QString::fromStdString(ent.GetName());
}
return retVal;
}
This code will only work for public groups.
To get the private groups I use (after I populated the private dictionary):
QString duDicomDictionary::getTagName( const gdcm::PrivateTag & tag )
{
QString retVal;
const gdcm::Global& g = gdcm::Global::GetInstance();
const gdcm::Dicts &dicts = g.GetDicts();
const gdcm::PrivateDict &privdict = dicts.GetPrivateDict();
gdcm::DictEntry ent = privdict.GetDictEntry(tag);
if (ent.GetVR() != gdcm::VR::INVALID ) {
retVal = QString::fromStdString(ent.GetName());
}
else
{
ent = g_privateDict.GetDictEntry(tag);
if (ent.GetVR() != gdcm::VR::INVALID ) {
retVal = QString::fromStdString(ent.GetName());
}
}
return retVal;
}

sourceModel property in QSortFilterPtoxyModel - Need to understand this code

I was going over the example here and I came across this example
bool MySortFilterProxyModel::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
QVariant leftData = sourceModel()->data(left);
QVariant rightData = sourceModel()->data(right);
if (leftData.type() == QVariant::DateTime) {
return leftData.toDateTime() < rightData.toDateTime();
} else {
QRegExp *emailPattern = new QRegExp("([\\w\\.]*#[\\w\\.]*)");
QString leftString = leftData.toString();
if(left.column() == 1 && emailPattern->indexIn(leftString) != -1)
leftString = emailPattern->cap(1);
QString rightString = rightData.toString();
if(right.column() == 1 && emailPattern->indexIn(rightString) != -1)
rightString = emailPattern->cap(1);
return QString::localeAwareCompare(leftString, rightString) < 0;
}
}
Although I understand the example I dont get where the sourceModel object is declared and where it came from?
Qt docs say about sourceModel() function:
"Returns the model that contains the data that is available through the proxy model.".
It stored internally in the proxy model and is the member function of QAbstractProxyModel class.