src/eric7/UI/NumbersWidget.py

Thu, 25 May 2023 19:51:47 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Thu, 25 May 2023 19:51:47 +0200
branch
eric7
changeset 10069
435cc5875135
parent 9653
e67609152c5e
child 10433
328f3ec4b77a
permissions
-rw-r--r--

Corrected and checked some code style issues (unused function arguments).

# -*- coding: utf-8 -*-

# Copyright (c) 2010 - 2023 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a widget to show numbers in different formats.
"""

from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import QHeaderView, QWidget

from eric7.EricGui import EricPixmapCache
from eric7.EricWidgets.EricApplication import ericApp

from .Ui_NumbersWidget import Ui_NumbersWidget


class BinaryModel(QAbstractTableModel):
    """
    Class implementing a model for entering binary numbers.
    """

    def __init__(self, parent=None):
        """
        Constructor

        @param parent reference to the parent widget (QWidget)
        """
        super().__init__(parent)

        self.__bits = 0
        self.__value = 0

    def rowCount(self, parent):  # noqa: U100
        """
        Public method to get the number of rows of the model.

        @param parent parent index (QModelIndex)
        @return number of columns (integer)
        """
        return 1

    def columnCount(self, parent):  # noqa: U100
        """
        Public method to get the number of columns of the model.

        @param parent parent index (QModelIndex)
        @return number of columns (integer)
        """
        return self.__bits

    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        """
        Public method to get data from the model.

        @param index index to get data for (QModelIndex)
        @param role role of the data to retrieve (integer)
        @return requested data
        """
        if role == Qt.ItemDataRole.CheckStateRole:
            if (self.__value >> (self.__bits - index.column() - 1)) & 1:
                return Qt.CheckState.Checked
            else:
                return Qt.CheckState.Unchecked

        elif role == Qt.ItemDataRole.DisplayRole:
            return ""

        return None

    def flags(self, index):  # noqa: U100
        """
        Public method to get flags from the model.

        @param index index to get flags for (QModelIndex)
        @return flags (Qt.ItemFlags)
        """
        return (
            Qt.ItemFlag.ItemIsUserCheckable
            | Qt.ItemFlag.ItemIsEnabled
            | Qt.ItemFlag.ItemIsSelectable
        )

    def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
        """
        Public method to get header data from the model.

        @param section section number (integer)
        @param orientation orientation (Qt.Orientation)
        @param role role of the data to retrieve (Qt.ItemDataRole)
        @return requested data
        """
        if (
            orientation == Qt.Orientation.Horizontal
            and role == Qt.ItemDataRole.DisplayRole
        ):
            return str(self.__bits - section - 1)

        return QAbstractTableModel.headerData(self, section, orientation, role)

    def setBits(self, bits):
        """
        Public slot to set the number of bits.

        @param bits number of bits to show (integer)
        """
        self.beginResetModel()
        self.__bits = bits
        self.endResetModel()

    def setValue(self, value):
        """
        Public slot to set the value to show.

        @param value value to show (integer)
        """
        self.beginResetModel()
        self.__value = value
        self.endResetModel()

    def setBitsAndValue(self, bits, value):
        """
        Public slot to set the number of bits and the value to show.

        @param bits number of bits to show (integer)
        @param value value to show (integer)
        """
        self.__bits = bits
        self.__value = value
        self.beginResetModel()
        self.endResetModel()

    def getValue(self):
        """
        Public slot to get the current value.

        @return current value of the model (integer)
        """
        return self.__value

    def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
        """
        Public method to set the data of a node cell.

        @param index index of the node cell (QModelIndex)
        @param value value to be set
        @param role role of the data (integer)
        @return flag indicating success (boolean)
        """
        if role == Qt.ItemDataRole.CheckStateRole:
            if Qt.CheckState(value) == Qt.CheckState.Checked:
                self.__value |= 1 << self.__bits - index.column() - 1
            else:
                self.__value &= ~(1 << self.__bits - index.column() - 1)
            self.dataChanged.emit(index, index)
            return True

        return False


class NumbersWidget(QWidget, Ui_NumbersWidget):
    """
    Class implementing a widget to show numbers in different formats.

    @signal insertNumber(str) emitted after the user has entered a number
            and selected the number format
    """

    insertNumber = pyqtSignal(str)

    def __init__(self, parent=None):
        """
        Constructor

        @param parent reference to the parent widget (QWidget)
        """
        super().__init__(parent)
        self.setupUi(self)

        self.setWindowIcon(EricPixmapCache.getIcon("eric"))

        self.__badNumberSheet = (
            "background-color: #b31b1b;"
            if ericApp().usesDarkPalette()
            else "background-color: #ffa0a0;"
        )

        self.binInButton.setIcon(EricPixmapCache.getIcon("2downarrow"))
        self.binOutButton.setIcon(EricPixmapCache.getIcon("2uparrow"))
        self.octInButton.setIcon(EricPixmapCache.getIcon("2downarrow"))
        self.octOutButton.setIcon(EricPixmapCache.getIcon("2uparrow"))
        self.decInButton.setIcon(EricPixmapCache.getIcon("2downarrow"))
        self.decOutButton.setIcon(EricPixmapCache.getIcon("2uparrow"))
        self.hexInButton.setIcon(EricPixmapCache.getIcon("2downarrow"))
        self.hexOutButton.setIcon(EricPixmapCache.getIcon("2uparrow"))

        self.formatBox.addItem(self.tr("Auto"), 0)
        self.formatBox.addItem(self.tr("Dec"), 10)
        self.formatBox.addItem(self.tr("Hex"), 16)
        self.formatBox.addItem(self.tr("Oct"), 8)
        self.formatBox.addItem(self.tr("Bin"), 2)

        self.sizeBox.addItem("8", 8)
        self.sizeBox.addItem("16", 16)
        self.sizeBox.addItem("32", 32)
        self.sizeBox.addItem("64", 64)

        self.__input = 0
        self.__inputValid = True
        self.__bytes = 1

        self.__model = BinaryModel(self)
        self.binTable.setModel(self.__model)
        self.binTable.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeMode.ResizeToContents
        )
        self.__model.setBitsAndValue(self.__bytes * 8, self.__input)
        self.__model.dataChanged.connect(self.__binModelDataChanged)

    def __formatNumbers(self, numberFormat):
        """
        Private method to format the various number inputs.

        @param numberFormat number format indicator (integer)
        """
        self.__block(True)

        self.binEdit.setStyleSheet("")
        self.octEdit.setStyleSheet("")
        self.decEdit.setStyleSheet("")
        self.hexEdit.setStyleSheet("")

        # determine byte count
        byteCount = 8
        tmp = self.__input
        for _ in range(8):
            c = (tmp & 0xFF00000000000000) >> 7 * 8
            if c != 0 and self.__input >= 0:
                break
            if c != 0xFF and self.__input < 0:
                break
            tmp <<= 8
            byteCount -= 1
        if byteCount == 0:
            byteCount = 1
        self.__bytes = byteCount

        bytesIn = self.sizeBox.itemData(self.sizeBox.currentIndex()) // 8
        if bytesIn and byteCount > bytesIn:
            self.sizeBox.setStyleSheet(self.__badNumberSheet)
        else:
            self.sizeBox.setStyleSheet("")

        # octal
        if numberFormat != 8:
            self.octEdit.setText("{0:0{1}o}".format(self.__input, bytesIn * 3))

        # decimal
        if numberFormat != 10:
            self.decEdit.setText("{0:d}".format(self.__input))

        # hexadecimal
        if numberFormat != 16:
            self.hexEdit.setText("{0:0{1}x}".format(self.__input, bytesIn * 2))

        # octal
        if numberFormat != 8:
            self.octEdit.setText("{0:0{1}o}".format(self.__input, bytesIn * 3))

        # binary
        if numberFormat != 2:
            num = "{0:0{1}b}".format(self.__input, bytesIn * 8)
            self.binEdit.setText(num)

        self.__model.setBitsAndValue(len(self.binEdit.text()), self.__input)

        self.__block(False)

    def __block(self, b):
        """
        Private slot to block some signals.

        @param b flah indicating the blocking state (boolean)
        """
        self.hexEdit.blockSignals(b)
        self.decEdit.blockSignals(b)
        self.octEdit.blockSignals(b)
        self.binEdit.blockSignals(b)
        self.binTable.blockSignals(b)

    @pyqtSlot(int)
    def on_sizeBox_valueChanged(self, value):
        """
        Private slot handling a change of the bit size.

        @param value selected bit size (integer)
        """
        self.__formatNumbers(10)

    @pyqtSlot()
    def on_byteOrderButton_clicked(self):
        """
        Private slot to swap the byte order.
        """
        bytesIn = self.sizeBox.itemData(self.sizeBox.currentIndex()) // 8
        if bytesIn == 0:
            bytesIn = self.__bytes

        tmp1 = self.__input
        tmp2 = 0
        for _ in range(bytesIn):
            tmp2 <<= 8
            tmp2 |= tmp1 & 0xFF
            tmp1 >>= 8

        self.__input = tmp2
        self.__formatNumbers(0)

    @pyqtSlot()
    def on_binInButton_clicked(self):
        """
        Private slot to retrieve a binary number from the current editor.
        """
        number = ericApp().getObject("ViewManager").getNumber()
        if number == "":
            return

        self.binEdit.setText(number)
        self.binEdit.setFocus()

    @pyqtSlot(str)
    def on_binEdit_textChanged(self, txt):
        """
        Private slot to handle input of a binary number.

        @param txt text entered (string)
        """
        try:
            self.__input = int(txt, 2)
            self.__inputValid = True
        except ValueError:
            self.__inputValid = False

        if self.__inputValid:
            self.__formatNumbers(2)
        else:
            self.binEdit.setStyleSheet(self.__badNumberSheet)

    @pyqtSlot()
    def on_binOutButton_clicked(self):
        """
        Private slot to send a binary number.
        """
        self.insertNumber.emit(self.binEdit.text())

    @pyqtSlot(QModelIndex, QModelIndex)
    def __binModelDataChanged(self, start, end):
        """
        Private slot to handle a change of the binary model value by the user.

        @param start start index (QModelIndex)
        @param end end index (QModelIndex)
        """
        val = self.__model.getValue()
        bytesIn = self.sizeBox.itemData(self.sizeBox.currentIndex()) // 8
        num = "{0:0{1}b}".format(val, bytesIn * 8)
        self.binEdit.setText(num)

    @pyqtSlot()
    def on_octInButton_clicked(self):
        """
        Private slot to retrieve an octal number from the current editor.
        """
        number = ericApp().getObject("ViewManager").getNumber()
        if number == "":
            return

        self.octEdit.setText(number)
        self.octEdit.setFocus()

    @pyqtSlot(str)
    def on_octEdit_textChanged(self, txt):
        """
        Private slot to handle input of an octal number.

        @param txt text entered (string)
        """
        try:
            self.__input = int(txt, 8)
            self.__inputValid = True
        except ValueError:
            self.__inputValid = False

        if self.__inputValid:
            self.__formatNumbers(8)
        else:
            self.octEdit.setStyleSheet(self.__badNumberSheet)

    @pyqtSlot()
    def on_octOutButton_clicked(self):
        """
        Private slot to send an octal number.
        """
        self.insertNumber.emit(self.octEdit.text())

    @pyqtSlot()
    def on_decInButton_clicked(self):
        """
        Private slot to retrieve a decimal number from the current editor.
        """
        number = ericApp().getObject("ViewManager").getNumber()
        if number == "":
            return

        self.decEdit.setText(number)
        self.decEdit.setFocus()

    @pyqtSlot(str)
    def on_decEdit_textChanged(self, txt):
        """
        Private slot to handle input of a decimal number.

        @param txt text entered (string)
        """
        try:
            self.__input = int(txt, 10)
            self.__inputValid = True
        except ValueError:
            self.__inputValid = False

        if self.__inputValid:
            self.__formatNumbers(10)
        else:
            self.decEdit.setStyleSheet(self.__badNumberSheet)

    @pyqtSlot()
    def on_decOutButton_clicked(self):
        """
        Private slot to send a decimal number.
        """
        self.insertNumber.emit(self.decEdit.text())

    @pyqtSlot()
    def on_hexInButton_clicked(self):
        """
        Private slot to retrieve a hexadecimal number from the current editor.
        """
        number = ericApp().getObject("ViewManager").getNumber()
        if number == "":
            return

        self.hexEdit.setText(number)
        self.hexEdit.setFocus()

    @pyqtSlot(str)
    def on_hexEdit_textChanged(self, txt):
        """
        Private slot to handle input of a hexadecimal number.

        @param txt text entered (string)
        """
        try:
            self.__input = int(txt, 16)
            self.__inputValid = True
        except ValueError:
            self.__inputValid = False

        if self.__inputValid:
            self.__formatNumbers(16)
        else:
            self.hexEdit.setStyleSheet(self.__badNumberSheet)

    @pyqtSlot()
    def on_hexOutButton_clicked(self):
        """
        Private slot to send a hexadecimal number.
        """
        self.insertNumber.emit(self.hexEdit.text())

eric ide

mercurial