2014-10-27 18 views
6

Có rất nhiều câu hỏi được viết ở đây về giới hạn của QSpinBox khi sử dụng int làm kiểu dữ liệu của nó. Thường thì mọi người muốn hiển thị số lớn hơn. Trong trường hợp của tôi, tôi muốn có thể hiển thị một số nguyên 32 bit chưa ký trong hệ thập lục phân. Điều này có nghĩa là tôi muốn phạm vi của tôi là [0x0, 0xFFFFFFFF]. QSpinBox bình thường lớn nhất có thể là 0x7FFFFFFF. Trả lời câu hỏi của riêng tôi ở đây, giải pháp mà tôi đưa ra là chỉ cần buộc int được xử lý như một int không dấu, bằng cách thực hiện lại các chức năng hiển thị và xác nhận có liên quan.QSpinBox với Unsigned Int cho đầu vào Hex

+0

Chấp nhận câu trả lời của bạn. – nnb

Trả lời

6

Kết quả khá đơn giản và hoạt động tốt. Chia sẻ ở đây trong trường hợp bất kỳ ai khác có thể hưởng lợi từ việc này. Nó có chế độ 32 bit và chế độ 16 bit.

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 vi phạm [quy tắc theo đuổi nghiêm ngặt] (http://eel.is/c++draft/basic.lval#8). Vì vậy, mã có chứa UB – yrHeTaTeJlb

1

tôi đã đưa ra vấn đề tương tự nhưng sử dụng PyQt vì vậy tôi không thể tránh phạm vi kiểm tra rằng Qt đang làm trong C dưới mui xe.

Cách giải quyết là sử dụng QDoulbeSpinbox và để truyền giá trị thành int trong textFromValue.

Đây là mã của tôi (nó cũng thực hiện một menu chuột phải để thay đổi cơ sở hiển thị):

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

Thú vị "bên" lưu ý, điều này không hoạt động trên 'PySide' bạn sẽ nhận được một' OverflowError: Python int quá lớn để chuyển đổi sang C dài' Ngoại lệ –

0

Tôi biết đây là một câu trả lời cũ nhưng đến đây từ google. Đây là giải pháp của tôi với pyside 1.2.4 dựa hơi tắt của giải pháp Techniquab nhưng không có vấn đề tràn số nguyên:

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

Nếu bạn không cần phải đầy đủ 32 bit, bạn có thể làm điều đó rất đơn giản như thế này:

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

Trong thiết kế mẫu (hoặc bất kỳ) sau đó bạn chỉ cần thiết lập:

  • prefix: 0x
  • displayIntegerBase: 16
  • maximum: 255 (hoặc bất kỳ)

Nếu bạn cần đầy đủ 32 bit bạn sẽ phải sử dụng thủ thuật đúc, hoặc có thể chỉ cần sử dụng một dòng chỉnh sửa.

Các vấn đề liên quan