使用箭头键时根据光标位置更改 QDoubleSpinBox 中的 singleStep

Change singleStep in a QDoubleSpinBox depending on the cursor position when using arrow keys

我想使 QDoubleSpinBox 可通过箭头键控制,即在选中框时按下向上键时增加值,按下时减小值。我目前通过将 singleStep 设置为某个值来做到这一点。但是,我希望能够在编辑字段时根据光标位置更改 singleStep 的值。

例如一个有两位小数的框,1.23,如果光标|在第一位小数1.2|3我想增加或减少0.1但是如果光标位于小数点后第二位 1.23| 我只想 increase/decrease 乘以 0.01.

注意:不确定是否相关,但我目前将 keyboardTracking 设置为 False 以避免在通过数字键输入值编辑值时发出信号。使用错误键时仍会发出这些值。

实现这一点的最简单方法可能是创建 QDoubleSpinBox 的子类并覆盖虚拟 stepBy() 方法。每次要增加或减少该值时都会调用它。这是一个简单的示例,展示了如何为整数旋转框执行此操作。对于浮点值,只需将 singleStep 大小固定为最小小数步长,并在 stepBy() 中更仔细地解析文本,将小数点考虑在内。

class MySpinBox : public QSpinBox
{
public:
    explicit MySpinBox(QWidget *parent = nullptr)
        : QSpinBox(parent) {
        setMaximum(9999);
        setValue(5000);
    };

    void stepBy(int steps) override {
        const int cursorPos = qMax(lineEdit()->cursorPosition(), 1);
        const int textLen = lineEdit()->text().length();
        for (int i = textLen - cursorPos; i > 0; i--)
            steps *= 10;
        QAbstractSpinBox::stepBy(steps);
        lineEdit()->setCursorPosition(cursorPos);
    }
};

之后,我也在 PyQt 中重新定义了 stepBy。由于我正在寻找适用于浮点数和负值的解决方案,因此我必须处理一些特殊情况,并通过重新定义 textFromValue 来显示正值的 + 号。特殊情况在下面的解决方案中评论:

import sys
from PyQt5.QtWidgets import QDoubleSpinBox, QMainWindow, QApplication, QLabel


class MyQDoubleSpinBox(QDoubleSpinBox):
    def textFromValue(self, value):
        # show + sign for positive values
        text = super().textFromValue(value)
        if value >= 0:
            text = "+" + text
        return text

    def stepBy(self, steps):
        cursor_position = self.lineEdit().cursorPosition()
        # number of characters before the decimal separator including +/- sign
        n_chars_before_sep = len(str(abs(int(self.value())))) + 1
        if cursor_position == 0:
            # set the cursor right of the +/- sign
            self.lineEdit().setCursorPosition(1)
            cursor_position = self.lineEdit().cursorPosition()
        single_step = 10 ** (n_chars_before_sep - cursor_position)
        # Handle decimal separator. Step should be 0.1 if cursor is at `1.|23` or
        # `1.2|3`.
        if cursor_position >= n_chars_before_sep + 2:
            single_step = 10 * single_step
        # Change single step and perform the step
        self.setSingleStep(single_step)
        super().stepBy(steps)
        # Undo selection of the whole text.
        self.lineEdit().deselect()
        # Handle cases where the number of characters before the decimal separator
        # changes. Step size should remain the same.
        new_n_chars_before_sep = len(str(abs(int(self.value())))) + 1
        if new_n_chars_before_sep < n_chars_before_sep:
            cursor_position -= 1
        elif new_n_chars_before_sep > n_chars_before_sep:
            cursor_position += 1
        self.lineEdit().setCursorPosition(cursor_position)


class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setGeometry(100, 100, 600, 400)
        self.UiComponents()
        self.show()

    def UiComponents(self):
        self.spin = MyQDoubleSpinBox(self)
        self.spin.setGeometry(100, 100, 150, 40)
        self.spin.setRange(-10000, 10000)
        self.spin.setValue(50)
        self.spin.setKeyboardTracking(False)


if __name__ == "__main__":
    App = QApplication(sys.argv)
    window = Window()
    sys.exit(App.exec())