src/eric7/UI/NumbersWidget.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 9161
90939b08da20
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2010 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a widget to show numbers in different formats.
8 """
9
10 from PyQt6.QtCore import pyqtSlot, pyqtSignal, Qt, QAbstractTableModel
11 from PyQt6.QtWidgets import QWidget, QHeaderView
12
13 from EricWidgets.EricApplication import ericApp
14
15 from .Ui_NumbersWidget import Ui_NumbersWidget
16
17 import UI.PixmapCache
18
19
20 class BinaryModel(QAbstractTableModel):
21 """
22 Class implementing a model for entering binary numbers.
23 """
24 def __init__(self, parent=None):
25 """
26 Constructor
27
28 @param parent reference to the parent widget (QWidget)
29 """
30 super().__init__(parent)
31
32 self.__bits = 0
33 self.__value = 0
34
35 def rowCount(self, parent):
36 """
37 Public method to get the number of rows of the model.
38
39 @param parent parent index (QModelIndex)
40 @return number of columns (integer)
41 """
42 return 1
43
44 def columnCount(self, parent):
45 """
46 Public method to get the number of columns of the model.
47
48 @param parent parent index (QModelIndex)
49 @return number of columns (integer)
50 """
51 return self.__bits
52
53 def data(self, index, role=Qt.ItemDataRole.DisplayRole):
54 """
55 Public method to get data from the model.
56
57 @param index index to get data for (QModelIndex)
58 @param role role of the data to retrieve (integer)
59 @return requested data
60 """
61 if role == Qt.ItemDataRole.CheckStateRole:
62 if (self.__value >> (self.__bits - index.column() - 1)) & 1:
63 return Qt.CheckState.Checked
64 else:
65 return Qt.CheckState.Unchecked
66
67 elif role == Qt.ItemDataRole.DisplayRole:
68 return ""
69
70 return None
71
72 def flags(self, index):
73 """
74 Public method to get flags from the model.
75
76 @param index index to get flags for (QModelIndex)
77 @return flags (Qt.ItemFlags)
78 """
79 return (
80 Qt.ItemFlag.ItemIsUserCheckable |
81 Qt.ItemFlag.ItemIsEnabled |
82 Qt.ItemFlag.ItemIsSelectable
83 )
84
85 def headerData(self, section, orientation,
86 role=Qt.ItemDataRole.DisplayRole):
87 """
88 Public method to get header data from the model.
89
90 @param section section number (integer)
91 @param orientation orientation (Qt.Orientation)
92 @param role role of the data to retrieve (Qt.ItemDataRole)
93 @return requested data
94 """
95 if (
96 orientation == Qt.Orientation.Horizontal and
97 role == Qt.ItemDataRole.DisplayRole
98 ):
99 return str(self.__bits - section - 1)
100
101 return QAbstractTableModel.headerData(self, section, orientation, role)
102
103 def setBits(self, bits):
104 """
105 Public slot to set the number of bits.
106
107 @param bits number of bits to show (integer)
108 """
109 self.beginResetModel()
110 self.__bits = bits
111 self.endResetModel()
112
113 def setValue(self, value):
114 """
115 Public slot to set the value to show.
116
117 @param value value to show (integer)
118 """
119 self.beginResetModel()
120 self.__value = value
121 self.endResetModel()
122
123 def setBitsAndValue(self, bits, value):
124 """
125 Public slot to set the number of bits and the value to show.
126
127 @param bits number of bits to show (integer)
128 @param value value to show (integer)
129 """
130 self.__bits = bits
131 self.__value = value
132 self.beginResetModel()
133 self.endResetModel()
134
135 def getValue(self):
136 """
137 Public slot to get the current value.
138
139 @return current value of the model (integer)
140 """
141 return self.__value
142
143 def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
144 """
145 Public method to set the data of a node cell.
146
147 @param index index of the node cell (QModelIndex)
148 @param value value to be set
149 @param role role of the data (integer)
150 @return flag indicating success (boolean)
151 """
152 if role == Qt.ItemDataRole.CheckStateRole:
153 if Qt.CheckState(value) == Qt.CheckState.Checked:
154 self.__value |= (1 << self.__bits - index.column() - 1)
155 else:
156 self.__value &= ~(1 << self.__bits - index.column() - 1)
157 self.dataChanged.emit(index, index)
158 return True
159
160 return False
161
162
163 class NumbersWidget(QWidget, Ui_NumbersWidget):
164 """
165 Class implementing a widget to show numbers in different formats.
166
167 @signal insertNumber(str) emitted after the user has entered a number
168 and selected the number format
169 """
170 insertNumber = pyqtSignal(str)
171
172 def __init__(self, parent=None):
173 """
174 Constructor
175
176 @param parent reference to the parent widget (QWidget)
177 """
178 super().__init__(parent)
179 self.setupUi(self)
180
181 self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
182
183 self.__badNumberSheet = (
184 "background-color: #b31b1b;"
185 if ericApp().usesDarkPalette() else
186 "background-color: #ffa0a0;"
187 )
188
189 self.binInButton.setIcon(UI.PixmapCache.getIcon("2downarrow"))
190 self.binOutButton.setIcon(UI.PixmapCache.getIcon("2uparrow"))
191 self.octInButton.setIcon(UI.PixmapCache.getIcon("2downarrow"))
192 self.octOutButton.setIcon(UI.PixmapCache.getIcon("2uparrow"))
193 self.decInButton.setIcon(UI.PixmapCache.getIcon("2downarrow"))
194 self.decOutButton.setIcon(UI.PixmapCache.getIcon("2uparrow"))
195 self.hexInButton.setIcon(UI.PixmapCache.getIcon("2downarrow"))
196 self.hexOutButton.setIcon(UI.PixmapCache.getIcon("2uparrow"))
197
198 self.formatBox.addItem(self.tr("Auto"), 0)
199 self.formatBox.addItem(self.tr("Dec"), 10)
200 self.formatBox.addItem(self.tr("Hex"), 16)
201 self.formatBox.addItem(self.tr("Oct"), 8)
202 self.formatBox.addItem(self.tr("Bin"), 2)
203
204 self.sizeBox.addItem("8", 8)
205 self.sizeBox.addItem("16", 16)
206 self.sizeBox.addItem("32", 32)
207 self.sizeBox.addItem("64", 64)
208
209 self.__input = 0
210 self.__inputValid = True
211 self.__bytes = 1
212
213 self.__model = BinaryModel(self)
214 self.binTable.setModel(self.__model)
215 self.binTable.horizontalHeader().setSectionResizeMode(
216 QHeaderView.ResizeMode.ResizeToContents)
217 self.__model.setBitsAndValue(self.__bytes * 8, self.__input)
218 self.__model.dataChanged.connect(self.__binModelDataChanged)
219
220 def __formatNumbers(self, numberFormat):
221 """
222 Private method to format the various number inputs.
223
224 @param numberFormat number format indicator (integer)
225 """
226 self.__block(True)
227
228 self.binEdit.setStyleSheet("")
229 self.octEdit.setStyleSheet("")
230 self.decEdit.setStyleSheet("")
231 self.hexEdit.setStyleSheet("")
232
233 # determine byte count
234 byteCount = 8
235 tmp = self.__input
236 for _ in range(8):
237 c = (tmp & 0xff00000000000000) >> 7 * 8
238 if c != 0 and self.__input >= 0:
239 break
240 if c != 0xff and self.__input < 0:
241 break
242 tmp <<= 8
243 byteCount -= 1
244 if byteCount == 0:
245 byteCount = 1
246 self.__bytes = byteCount
247
248 bytesIn = self.sizeBox.itemData(self.sizeBox.currentIndex()) // 8
249 if bytesIn and byteCount > bytesIn:
250 self.sizeBox.setStyleSheet(self.__badNumberSheet)
251 else:
252 self.sizeBox.setStyleSheet("")
253
254 # octal
255 if numberFormat != 8:
256 self.octEdit.setText("{0:0{1}o}".format(self.__input, bytesIn * 3))
257
258 # decimal
259 if numberFormat != 10:
260 self.decEdit.setText("{0:d}".format(self.__input))
261
262 # hexadecimal
263 if numberFormat != 16:
264 self.hexEdit.setText("{0:0{1}x}".format(self.__input, bytesIn * 2))
265
266 # octal
267 if numberFormat != 8:
268 self.octEdit.setText("{0:0{1}o}".format(self.__input, bytesIn * 3))
269
270 # binary
271 if numberFormat != 2:
272 num = "{0:0{1}b}".format(self.__input, bytesIn * 8)
273 self.binEdit.setText(num)
274
275 self.__model.setBitsAndValue(len(self.binEdit.text()), self.__input)
276
277 self.__block(False)
278
279 def __block(self, b):
280 """
281 Private slot to block some signals.
282
283 @param b flah indicating the blocking state (boolean)
284 """
285 self.hexEdit.blockSignals(b)
286 self.decEdit.blockSignals(b)
287 self.octEdit.blockSignals(b)
288 self.binEdit.blockSignals(b)
289 self.binTable.blockSignals(b)
290
291 @pyqtSlot(int)
292 def on_sizeBox_valueChanged(self, value):
293 """
294 Private slot handling a change of the bit size.
295
296 @param value selected bit size (integer)
297 """
298 self.__formatNumbers(10)
299
300 @pyqtSlot()
301 def on_byteOrderButton_clicked(self):
302 """
303 Private slot to swap the byte order.
304 """
305 bytesIn = self.sizeBox.itemData(self.sizeBox.currentIndex()) // 8
306 if bytesIn == 0:
307 bytesIn = self.__bytes
308
309 tmp1 = self.__input
310 tmp2 = 0
311 for _ in range(bytesIn):
312 tmp2 <<= 8
313 tmp2 |= tmp1 & 0xff
314 tmp1 >>= 8
315
316 self.__input = tmp2
317 self.__formatNumbers(0)
318
319 @pyqtSlot()
320 def on_binInButton_clicked(self):
321 """
322 Private slot to retrieve a binary number from the current editor.
323 """
324 number = ericApp().getObject("ViewManager").getNumber()
325 if number == "":
326 return
327
328 self.binEdit.setText(number)
329 self.binEdit.setFocus()
330
331 @pyqtSlot(str)
332 def on_binEdit_textChanged(self, txt):
333 """
334 Private slot to handle input of a binary number.
335
336 @param txt text entered (string)
337 """
338 try:
339 self.__input = int(txt, 2)
340 self.__inputValid = True
341 except ValueError:
342 self.__inputValid = False
343
344 if self.__inputValid:
345 self.__formatNumbers(2)
346 else:
347 self.binEdit.setStyleSheet(self.__badNumberSheet)
348
349 @pyqtSlot()
350 def on_binOutButton_clicked(self):
351 """
352 Private slot to send a binary number.
353 """
354 self.insertNumber.emit(self.binEdit.text())
355
356 def __binModelDataChanged(self, start, end):
357 """
358 Private slot to handle a change of the binary model value by the user.
359
360 @param start start index (QModelIndex)
361 @param end end index (QModelIndex)
362 """
363 val = self.__model.getValue()
364 bytesIn = self.sizeBox.itemData(self.sizeBox.currentIndex()) // 8
365 num = "{0:0{1}b}".format(val, bytesIn * 8)
366 self.binEdit.setText(num)
367
368 @pyqtSlot()
369 def on_octInButton_clicked(self):
370 """
371 Private slot to retrieve an octal number from the current editor.
372 """
373 number = ericApp().getObject("ViewManager").getNumber()
374 if number == "":
375 return
376
377 self.octEdit.setText(number)
378 self.octEdit.setFocus()
379
380 @pyqtSlot(str)
381 def on_octEdit_textChanged(self, txt):
382 """
383 Private slot to handle input of an octal number.
384
385 @param txt text entered (string)
386 """
387 try:
388 self.__input = int(txt, 8)
389 self.__inputValid = True
390 except ValueError:
391 self.__inputValid = False
392
393 if self.__inputValid:
394 self.__formatNumbers(8)
395 else:
396 self.octEdit.setStyleSheet(self.__badNumberSheet)
397
398 @pyqtSlot()
399 def on_octOutButton_clicked(self):
400 """
401 Private slot to send an octal number.
402 """
403 self.insertNumber.emit(self.octEdit.text())
404
405 @pyqtSlot()
406 def on_decInButton_clicked(self):
407 """
408 Private slot to retrieve a decimal number from the current editor.
409 """
410 number = ericApp().getObject("ViewManager").getNumber()
411 if number == "":
412 return
413
414 self.decEdit.setText(number)
415 self.decEdit.setFocus()
416
417 @pyqtSlot(str)
418 def on_decEdit_textChanged(self, txt):
419 """
420 Private slot to handle input of a decimal number.
421
422 @param txt text entered (string)
423 """
424 try:
425 self.__input = int(txt, 10)
426 self.__inputValid = True
427 except ValueError:
428 self.__inputValid = False
429
430 if self.__inputValid:
431 self.__formatNumbers(10)
432 else:
433 self.decEdit.setStyleSheet(self.__badNumberSheet)
434
435 @pyqtSlot()
436 def on_decOutButton_clicked(self):
437 """
438 Private slot to send a decimal number.
439 """
440 self.insertNumber.emit(self.decEdit.text())
441
442 @pyqtSlot()
443 def on_hexInButton_clicked(self):
444 """
445 Private slot to retrieve a hexadecimal number from the current editor.
446 """
447 number = ericApp().getObject("ViewManager").getNumber()
448 if number == "":
449 return
450
451 self.hexEdit.setText(number)
452 self.hexEdit.setFocus()
453
454 @pyqtSlot(str)
455 def on_hexEdit_textChanged(self, txt):
456 """
457 Private slot to handle input of a hexadecimal number.
458
459 @param txt text entered (string)
460 """
461 try:
462 self.__input = int(txt, 16)
463 self.__inputValid = True
464 except ValueError:
465 self.__inputValid = False
466
467 if self.__inputValid:
468 self.__formatNumbers(16)
469 else:
470 self.hexEdit.setStyleSheet(self.__badNumberSheet)
471
472 @pyqtSlot()
473 def on_hexOutButton_clicked(self):
474 """
475 Private slot to send a hexadecimal number.
476 """
477 self.insertNumber.emit(self.hexEdit.text())

eric ide

mercurial