Chipmuenk
Chipmuenk

Reputation: 697

Limit number of displayed float digits in pyQT QLineEdit without sacrificing internal accuracy

I'm using QLineEdit widgets in my application to enter and edit numeric (float) values. I would like to display a rounded version of the float value while keeping the full internal accuracy. Only when editing a QLineEdit field, the full number of digits should be displayed.

This is needed for three reasons:

Does anybody know a neat solution for this problem?

Below you find a MWE, the full code (pyfda) uses dynamic instantiation of widgets and other ugly stuff.

# -*- coding: utf-8 -*-
from __future__ import print_function, division
import numpy as np
import sys
from PyQt4 import QtGui

class InputNumFields(QtGui.QWidget):

    def __init__(self, parent):
        super(InputNumFields, self).__init__(parent)
        self.edit_input_float = 10*np.log10(np.pi) # store in log format
        self._init_UI()

    def _init_UI(self):    
        self.edit_input = QtGui.QLineEdit()
        self.edit_input.editingFinished.connect(self.store_entries)
        self.lay_g_main = QtGui.QGridLayout()
        self.lay_g_main.addWidget(self.edit_input, 0, 0)
        self.setLayout(self.lay_g_main)
        self.get_entries()

    def store_entries(self):
        """ Store text entry as log float"""
        self.edit_input_float = 10*np.log10(float(self.edit_input.text()))
        self.get_entries()

    def get_entries(self):
        """ Retrieve float value, delog and convert to string """
        self.edit_input.setText(str(10**(self.edit_input_float/10)))

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    mainw = InputNumFields(None)
    app.setActiveWindow(mainw) 
    mainw.show()
    sys.exit(app.exec_())

Upvotes: 1

Views: 2431

Answers (2)

Chipmuenk
Chipmuenk

Reputation: 697

I have tried to use a mix of signal-slot connections (editingFinished) and eventFilter. However, I ran into some strange bugs that may have been caused by some race conditions or simply because the logic behind the code became too warped. Anyway, the following snippet works great, maybe it is helpful for somebody:

def eventFilter(self, source, event):
    """
    Filter all events generated by the QLineEdit widgets. Source and type
    of all events generated by monitored objects are passed to this eventFilter,
    evaluated and passed on to the next hierarchy level.

    - When a QLineEdit widget gains input focus (QEvent.FocusIn`), display
      the stored value with full precision
    - When a key is pressed inside the text field, set the `spec_edited` flag
      to True.
    - When a QLineEdit widget loses input focus (QEvent.FocusOut`), store
      current value in linear format with full precision (only if
      `spec_edited`== True) and display the stored value in selected format
    """
    if isinstance(source, QtGui.QLineEdit): # could be extended for other widgets
        if event.type() == QEvent.FocusIn:
            self.spec_edited = False
            self.get_entries()
        elif event.type() == QEvent.KeyPress:
            self.spec_edited = True
        elif event.type() == QEvent.FocusOut:
            self._store_entries(source)
    # Call base class method to continue normal event processing:
    return super(InputAmpSpecs, self).eventFilter(source, event)

Upvotes: 0

ekhumoro
ekhumoro

Reputation: 120608

It seems that the behaviour should be this:

  • When the line-edit loses input focus, display the stored value rounded
  • When the line-edit gains input focus, display the stored value in full
  • Whenever editing finishes, store the full current value in log format

This implies that rounding must not occur when return or enter is pressed (because the line-edit would not lose focus in that case).

The above behaviour can be achieved with the following changes:

from PyQt4 import QtCore, QtGui

class InputNumFields(QtGui.QWidget):
    ...

    def _init_UI(self):
        self.edit_input = QtGui.QLineEdit()
        self.edit_input.installEventFilter(self)
        ...

    def eventFilter(self, source, event):
        if (event.type() == QtCore.QEvent.FocusIn and
            source is self.edit_input):
            self.get_entries()
        return super(InputNumFields, self).eventFilter(source, event)

    def get_entries(self):
        value = 10**(self.edit_input_float/10)
        if not self.edit_input.hasFocus():
            value = round(value, 3)
        self.edit_input.setText(str(value))

PS:

You should probably add a button or something to your example, so you can test the effects of changing focus.

Upvotes: 1

Related Questions