2014-10-27 11 views
6

Существует множество вопросов, связанных с ограничением QSpinBox использованием int в качестве его типа данных. Часто люди хотят отображать большие числа. В моем случае я хочу показать 32-значное целое число без знака в шестнадцатеричном формате. Это означает, что мой диапазон будет [0x0, 0xFFFFFFFF]. Самый большой обычный QSpinBox может пойти 0x7FFFFFFF. Отвечая на мой собственный вопрос здесь, решение, которое я придумал, состоит в том, чтобы просто заставить int обрабатываться как unsigned int, переопределяя соответствующие функции отображения и проверки.QSpinBox с Unsigned Int для ввода Hex

+0

Примите ваш ответ. – nnb

ответ

6

Результат довольно прост, и он работает хорошо. Обмен здесь, если кто-то может воспользоваться этим. Он имеет 32-битный режим и 16-битный режим.

Example of HexSpinBox

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); 
    } 

}; 
+0

Ваш 'reinterpret_cast's нарушает [строгое правило опускания] (http://eel.is/c++draft/basic.lval#8). Итак, код содержит UB – yrHeTaTeJlb

1

Я пришел с той же проблемой, но с использованием PyQt, поэтому я не мог избежать проверки, что Qt делает в C под капотом диапазона.

Обходным путем было использовать QDoulbeSpinbox и передать значение int в textFromValue.

Вот мой код (он также реализует меню правой кнопки мыши, чтобы изменить основание дисплея):

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) 
+0

Интересная заметка «side», это не работает на 'PySide', вы получите' OverflowError: Python int too large для преобразования в C long Exception –

0

Я знаю, что это старый ответ, но пришел сюда из Google. Вот мое решение с PySide 1.2.4 на основе несколько от решения Techniquab, но не вопрос целочисленного переполнения:

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)) 
1

Если вам не нужен полный 32 бита вы можете сделать это очень просто, как это:

#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(); 
    } 
}; 

В виде конструктора (или любой другой), то вы только что установили:

  • prefix: 0x
  • displayIntegerBase: 16
  • maximum: 255 (или любой другой)

Если вам нужен полный 32 бит вам придется использовать литейные трюки, или, может быть, просто использовать строку редактирование.

Смежные вопросы