SpinBox с сигналом только по Enter

Задача: спинбокс должен принимать данные как текстовое поле (как QLineEdit) либо изменением введённых данных стрелками.
Но отдавать должен только по Enter.

Встроенный метод valueChanged() не подходит.
Метод editingFinished(), взятый у QAbstractSpinBox, лучше.
Но он отдаёт данные не только по Enter, но и по смене фокуса - это неприемлемо.

Также есть метод setKeyboardTracking(False), который работает хорошо для ручного ввода, но при использовании стрелок сигнал всё равно реагирует по каждому нажатию, т.е. не подходит.

Также можно вспомнить, что QSpinBox наследуется от QLineEdit и применить my_spinbox.lineEdit().returnPressed.connect(...). Этот метод работает в песочнице, но в реальном проекте нет, не буду углубляться.

Также есть вот такая ссылка где предлагается несколько решений, одно из них я привожу ниже (оно тоже на базе my_spinbox.lineEdit().returnPressed..., но там ещё и создание спинбокса в кастомном классе). В песочнице работает, в реальном проекте сигнал класса кастомного спинбокса не виден вообще, не понимаю почему.

Вопрос - что делать? Там по ссылке хорошие, вроде бы, решения типа отключения слота, но не пойму как их применить.



Update:

Проблема в том, что mySpinbox.lineEdit().returnPressed.connect() определяет нажатие key_Return (клавиша справа) как Enter,
а нажатия самой key_Enter не видит совсем.

В моём случае это неприемлемо. Я делал обычные спинбокс (определял в классе представления), делал отдельный класс с его кастомизацией, применял event() и eventFilter().

Это и есть: 'Этот метод работает в песочнице, но в реальном проекте нет, не буду углубляться'.

from PySide6.QtWidgets import QWidget, QVBoxLayout, QApplication, QSpinBox
from PySide6.QtCore import Signal
import sys



class MySpinBox(QSpinBox):
    valueHasChanged = Signal(int)
    def __init__(self, object_name:str):
        super().__init__()
        self.setObjectName(object_name)
        self.lineEdit().returnPressed.connect(self.spinBoxReturnPressed)

    def spinBoxReturnPressed(self):
        result = self.sender().text()
        self.valueHasChanged.emit(result)



class MyClass(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(300,200)
        self.sb1 = MySpinBox('_sb1_')
        self.sb1.setRange(-1000, 1000)
        self.sb1.valueHasChanged.connect(self.returnValue)
        self.sb2 = MySpinBox('_sb2_')
        self.sb2.setRange(-1000, 1000)
        self.sb2.valueHasChanged.connect(self.returnValue)
        vbox = QVBoxLayout()
        vbox.addWidget(self.sb1)
        vbox.addWidget(self.sb2)
        self.setLayout(vbox)

    def returnValue(self):
        value = self.sender().text()
        print(value, self.sender().objectName())





if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyClass()
    window.show()
    sys.exit(app.exec())

Ответы (1 шт):

Автор решения: S. Nick

Sorry, я не совсем понимаю, что у вас работает в песочнице, но в реальном проекте нет и почему.

Попробуйте мой пример и расскажите, что в нем не так.

'''
from PySide6.QtWidgets import QWidget, QVBoxLayout, \
    QApplication, QSpinBox
from PySide6.QtCore import Signal
'''
import sys
from PyQt5.Qt import *


class MySpinBox(QSpinBox):
# --------------------------> vvv    ?????? <----------------------
#    valueHasChanged = Signal(int)                        # PySide6

# -----------------------------> vvv ++++++ <----------------------
    valueHasChanged = pyqtSignal(str)                     # PyQt5
    
    def __init__(self, object_name:str):
        super().__init__()
        self.setObjectName(object_name)
        self.lineEdit().returnPressed.connect(self.spinBoxReturnPressed)

    def spinBoxReturnPressed(self):
        result = self.sender().text()
        self.valueHasChanged.emit(result)


class MyClass(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(300,200)
        self.sb1 = MySpinBox('_sb1_')
        self.sb1.setRange(-1000, 1000)
        self.sb1.valueHasChanged.connect(self.returnValue)
        self.sb2 = MySpinBox('_sb2_')
        self.sb2.setRange(-1000, 1000)
        self.sb2.valueHasChanged.connect(self.returnValue)

        vbox = QVBoxLayout(self)
        vbox.addWidget(self.sb1)
        vbox.addWidget(self.sb2)

    def returnValue(self, value):                         # +++ , value
# ?        value = self.sender().text()
        print(f'Значенин={value}; объект={self.sender().objectName()};\n')

# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv        
    def event(self, event):
        if event.type() == QEvent.KeyPress and event.key() in (
            Qt.Key_Enter,
            Qt.Key_Return,
        ):            
            self.focusNextPrevChild(True)
        return super().event(event)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyClass()
    window.show()
    sys.exit(app.exec())

введите сюда описание изображения

введите сюда описание изображения



Update:

Проблема в том, что mySpinbox.lineEdit().returnPressed.connect() определяет нажатие key_Return (клавиша справа) как Enter, а нажатия самой key_Enter не видит совсем.
В моём случае это неприемлемо. Я делал обычные спинбокс (определял в классе представления), делал отдельный класс с его кастомизацией, применял event() и eventFilter().
Это и есть: 'Этот метод работает в песочнице, но в реальном проекте нет, не буду углубляться'.

Я не могу проверить то, что вы пишите: "mySpinbox.lineEdit().returnPressed.connect() определяет нажатие key_Return (клавиша справа) как Enter, а нажатия самой key_Enter не видит совсем."

Попробуйте другой вариант решения вашей задачи:

'''
from PySide6.QtWidgets import QWidget, QVBoxLayout, \
    QApplication, QSpinBox
from PySide6.QtCore import Signal
'''
import sys
from PyQt5.Qt import *


class MySpinBox(QSpinBox):
# --------------------------> vvv  +++ <---------------------------
#    valueHasChanged = Signal(int, str)                   # PySide6

# -----------------------------> vvv  +++ ++++++ <----------------- !!!
    valueHasChanged = pyqtSignal(int, str)                # PyQt5

    def __init__(self, object_name:str):
        super().__init__()
        self.setObjectName(object_name)

# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv        
    def keyPressEvent(self, event):
        if event.key() in (Qt.Key_Enter, Qt.Key_Return):
            self.valueHasChanged.emit(self.value(), self.objectName())        
        super().keyPressEvent(event)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


class MyClass(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(300,200)
        self.sb1 = MySpinBox('_sb1_')
        self.sb1.setRange(-1000, 1000)
        self.sb1.valueHasChanged.connect(self.returnValue)
        self.sb2 = MySpinBox('_sb2_')
        self.sb2.setRange(-1000, 1000)
        self.sb2.valueHasChanged.connect(self.returnValue)

        vbox = QVBoxLayout(self)
        vbox.addWidget(self.sb1)
        vbox.addWidget(self.sb2)

    def returnValue(self, value, object):          # +++ , value, object
#       print(f'Значенин={value}; объект={self.sender().objectName()};\n')
       print(f'Значенин={value}; объект={object};\n')           # +++
       
    def event(self, event):
        if event.type() == QEvent.KeyPress and event.key() in (
            Qt.Key_Enter,
            Qt.Key_Return,
        ):            
            self.focusNextPrevChild(True)
        return super().event(event)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyClass()
    window.show()
    sys.exit(app.exec())

введите сюда описание изображения

→ Ссылка