In the default case qwtknob precision is two decimal places, I need to be accurate to three decimal places.
#include "knobtest.h"
knobtest::knobtest(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
connect(ui.Knob, SIGNAL(sliderMoved(double)), this, SLOT(knobChange(double)));
}
knobtest::~knobtest()
{
}
void knobtest::knobChange(double value)
{
ui.lcdNumber->display(value);
}
how should I write the code?
Related
Is there a property in Q(Double)SpinBox that allows the user to override digits that are right of the curser, just by typing?
For example: If the spinbox shows 12.52 I click between 1 and 2. Now I type 3.45 to get 13.45
Just checked it out - even activating Insert mode on keyboard doesn't help.
Apparently, you'll have to subclass QSPinBox and override some QAbstractSpinBox functions - at least, keyPressEvent.
QAbstractSpinBox has a pointer to underlying QLineEdit - which has a cursor position property.
There is no property, you can go with your own implementation like this:
Header:
#include <QObject>
#include <QDoubleSpinBox>
class CustomSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit CustomSpinBox(QWidget *parent = nullptr);
void keyPressEvent(QKeyEvent *event) override;
private:
void updateTextField(const QString &text);
};
Source:
#include "CustomSpinBox.h"
#include <QKeyEvent>
#include <QLineEdit>
CustomSpinBox::CustomSpinBox(QWidget *parent)
: QDoubleSpinBox(parent)
{}
void CustomSpinBox::keyPressEvent(QKeyEvent *event)
{
auto isInt = false;
event->text().toInt(&isInt);
if (isInt)
{
updateTextField(event->text());
}
else
{
QDoubleSpinBox::keyPressEvent(event);
}
}
void CustomSpinBox::updateTextField(const QString &text)
{
auto lineEdit = this->lineEdit();
auto cursorPosition = lineEdit->cursorPosition();
auto lineEditText = lineEdit->text();
QChar currentChar;
if (cursorPosition < lineEditText.size())
{
currentChar = lineEditText.at(cursorPosition);
if (currentChar.isPunct())
{
lineEdit->cursorForward(false);
}
}
lineEdit->del();
lineEdit->insert(text);
}
But please note that the behaviour can be counter-intuitive. What if the value is 1,23? With that approach, you cannot enter any value bigger than 9,(9). Maybe it is what is expected, maybe it should not jump over the dot/comma unless the value before the punctuation mark has size length bigger than 2 digits (or 3 or any other number).
I have a time-consuming computation that depends on a double value. For this, I've created a GUI where I can set the value for the computation using a QDoubleSpinBox. The double spin box QDoubleSpinBox::valueChanged(double) signal is connected to a slot that starts the heavy computation in a new thread using QtConcurrent::run. The problem is that when I add a progress bar, the double spin box automatically fills its contents (zero-padding until the number of decimals) when the progress bar appears. My feeling is that this is because the double spin box loses the focus (i.e. the progress bar is the selected widget).
My question is:
How can I show the progress bar and make the double spin box
not fill the rest of the decimals with zeros?
This video shows how, when the progress bar is not shown, I can keep editing the double spin box while, when the progress bar is shown, the double spin box fills its precision with zeros. This is the current behavior, not the desired one. The desired one is that after the computation is done the double spin box is has not automatically filled its empty decimal places with zeros. Here is the code used for the video (fully available in GitHub):
Header
class DoubleSpinboxHeavyComputation : public QWidget
{
Q_OBJECT
public:
explicit DoubleSpinboxHeavyComputation(QWidget *parent = nullptr);
~DoubleSpinboxHeavyComputation();
signals:
void computationDone();
void progressSignal(int progress_state);
private slots:
void startHeavyComputationInThread();
void heavyComputation();
private:
Ui::DoubleSpinboxHeavyComputation *ui;
int n_ = 0;
};
Implementation
DoubleSpinboxHeavyComputation::DoubleSpinboxHeavyComputation(QWidget *parent)
: QWidget(parent), ui(new Ui::DoubleSpinboxHeavyComputation)
{
ui->setupUi(this);
connect(ui->doubleSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
&DoubleSpinboxHeavyComputation::startHeavyComputationInThread);
}
DoubleSpinboxHeavyComputation::~DoubleSpinboxHeavyComputation()
{
delete ui;
}
void DoubleSpinboxHeavyComputation::startHeavyComputationInThread()
{
if (ui->checkBox->isChecked())
{
QProgressDialog *progress = new QProgressDialog("Computing", "", 0, 0, this);
progress->setWindowTitle(windowTitle());
progress->setWindowFlags((progress->windowFlags() | Qt::CustomizeWindowHint) &
~Qt::WindowCloseButtonHint); // Hide close button
progress->setWindowModality(Qt::WindowModal);
progress->setCancelButton(nullptr);
progress->setMaximum(1000);
progress->show();
connect(this, &DoubleSpinboxHeavyComputation::progressSignal, progress, &QProgressDialog::setValue);
connect(this, &DoubleSpinboxHeavyComputation::computationDone, progress, &QProgressDialog::close);
connect(this, &DoubleSpinboxHeavyComputation::computationDone, progress, &QProgressDialog::deleteLater);
}
QtConcurrent::run(this, &DoubleSpinboxHeavyComputation::heavyComputation);
}
void DoubleSpinboxHeavyComputation::heavyComputation()
{
int current_n = n_;
++n_;
qDebug() << "Start computation " << current_n;
for (int i = 0; i < 1000; i++)
{
emit progressSignal(i);
usleep(1000);
}
qDebug() << "End computation" << current_n;
emit computationDone();
}
Looks like you need to subclass your QDoubleSpinbox and reimplement textFromValue method
class NumericEdit : public QDoubleSpinBox
{
Q_OBJECT
public:
NumericEdit(QWidget *p_parent = nullptr);
QString textFromValue(double val) const;
};
QString NumericEdit::textFromValue(double val) const
{
//default converting
// return QString::number(val);
//converting with a local representation
QLocale locale;
return locale.toString(val);
}
But this will break default prefix and suffix functional of spinbox
QDoubleSpinBox shows the number using decimal point by default
and I'm trying to format it to scientific notation
My solution was to subclass QDoubleSpinBox and redefine methods validate, valueFromText and textFromValue.
class SciNotDoubleSpinbox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit SciNotDoubleSpinbox(QWidget *parent = 0) : QDoubleSpinBox(parent) {}
// Change the way we read the user input
double valueFromText(const QString & text) const
{
double numFromStr = text.toDouble();
return numFromStr;
}
// Change the way we show the internal number
QString textFromValue(double value) const
{
return QString::number(value, 'E', 6);
}
// Change the way we validate user input (if validate => valueFromText)
QValidator::State validate(QString &text, int&) const
{
// Try to convert the string to double
bool ok;
text.toDouble(&ok);
// See if it's a valid Double
QValidator::State validationState;
if(ok)
{
// If string conversion was valid, set as ascceptable
validationState = QValidator::Acceptable;
}
else
{
// If string conversion was invalid, set as invalid
validationState = QValidator::Invalid;
}
return validationState;
}
};
It works, but validate seems sloppy. It tries to convert the number to double using Qt's toDouble, and returns valid or invalid state depending if it was a successful conversion or not. It doesn't even use the position.
Is there a way to validate a string in scientific notation in a "cleaner" way?
I am building a C++ GUI application on QT Creator.
I changed the location to Portuguese/Brazil, now only comma is the decimal separator.
I need the QDoubleSpinBox to get as decimal separator dot and comma.
Officialy comma is the separator in Portuguese, but some keyboard only have points in the numeric part.
Please help,
subClass QDoubleSpinBox and reimplement the virtual method validate
full solution here :
customSpinBox.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QRegExpValidator>
#include <QDoubleSpinBox>
class CustomSpinBox : public QDoubleSpinBox {
Q_OBJECT
public:
explicit CustomSpinBox(QWidget* parent =0);
virtual QValidator::State validate(QString & text, int & pos) const;
private:
QRegExpValidator* validator;
};
#endif // WIDGET_H
customSpinBox.cpp
CustomSpinBox::CustomSpinBox(QWidget *parent):QDoubleSpinBox(parent),
validator(new QRegExpValidator(this))
{
validator->setRegExp(QRegExp("\\d{1,}(?:[,.]{1})\\d*"));
}
QValidator::State CustomSpinBox::validate(QString &text, int &pos) const
{
return validator->validate(text,pos);
}
I tried to converted the solution from basslo to Qt 6.0 but it failed to accept integer numbers. The following solution works fine for me with Qt 6.0. Also it did not work, wehen a suffix is set for the spin with i.e spinBox->setSuffix("mm");
My current solution below:
class CRelaxedDoubleSpinBox : public QDoubleSpinBox {
Q_OBJECT
bool doConvert = false;
QString decimalStr;
public:
explicit CRelaxedDoubleSpinBox(QWidget* parent =0, double minVal = -100, double maxVal = 100.) : QDoubleSpinBox(parent)
{
// setStepType(QAbstractSpinBox::AdaptiveDecimalStepType);
setMinimum(minVal);
setMaximum(maxVal);
setAlignment(Qt::AlignRight);
QLocale curLocale;
decimalStr = curLocale.decimalPoint();
doConvert = curLocale.decimalPoint() != ".";
}
virtual QValidator::State validate(QString & text, int & pos) const
{
QString s(text);
if(doConvert)
s = s.replace(".", decimalStr);
return QDoubleSpinBox::validate(s,pos);
}
double valueFromText(const QString& text) const
{
QString s(text);
if(doConvert)
s = s.replace(".", decimalStr);
return QDoubleSpinBox::valueFromText(s);
}
};
You can subclass QDoubleSpinBox and reimplement the validate method to accept both the dot and the comma as the decimal separator. I think you can get by with just adding a special check when the input is a period and allow it to be accepted (provided there are no other periods or commas in the string), but otherwise call the base class implementation. I haven't compiled or tested this, but I think this is pretty close:
MyDoubleSpinBox::validate (QString &input, int &pos)
{
if (input == ".")
{
return (text ().contains (".") || text ().contains (",")) ? QValidator::Invalid : QValidator::Acceptable;
}
return QDoubleSpinBox::validate (input, pos);
}
I'm using PySide6 and I adapted to the Python language the wonderful solution provided by #RED SOFT ADAIR to Python:
from PySide6.QtWidgets import QDoubleSpinBox,
class CustomDoubleSpinbox(QDoubleSpinBox):
def validate(self, text: str, pos: int) -> object:
text = text.replace(".", ",")
return QDoubleSpinBox.validate(self, text, pos)
def valueFromText(self, text: str) -> float:
text = text.replace(",", ".")
return float(text)
Here it is in case anyone ended up here in a similar situation.
Solution given by basslo does not work for me. I also need to reimplement the valueFromText method. Otherwise, when I press the enter key in the DoubleSpinBox, the value is set to 0.
Here is the class
doublespinboxwithcommasandpoints.h
#ifndef DOUBLESPINBOWITHCOMMASANDPOINTS_H
#define DOUBLESPINBOWITHCOMMASANDPOINTS_H
#include <QDoubleSpinBox>
#include <QRegularExpressionValidator>
class DoubleSpinBoxWithCommasAndPoints : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit DoubleSpinBoxWithCommasAndPoints(QWidget* parent = nullptr);
virtual ~DoubleSpinBoxWithCommasAndPoints();
QValidator::State validate(QString & text, int & pos) const override;
qreal valueFromText(const QString &text) const override;
private:
QRegularExpressionValidator* m_validator;
QString current_suffix;
};
#endif // DOUBLESPINBOWITHCOMMASANDPOINTS_H
doublespinboxwithcommasandpoints.cpp
#include "doublespinboxwithcommasandpoints.h"
DoubleSpinBoxWithCommasAndPoints::DoubleSpinBoxWithCommasAndPoints(QWidget *parent)
: QDoubleSpinBox(parent)
, m_validator(nullptr)
, current_suffix(QString())
{
// validate positive or negative number written with "." or ","
// and that may have a suffix at the end
// also match empty string "(?![\\s\\S]\r\n)" to allow user to clear the whole field
const QString regex = QStringLiteral("((?![\\s\\S]\r\n)|-?\\d{1,}(?:[,.]{1})?\\d*)");
m_validator = new QRegularExpressionValidator(QRegularExpression(regex), this);
// check if a suffix has been added
connect(this, &DoubleSpinBoxWithCommasAndPoints::textChanged,
[this](){
if(!suffix().isEmpty()) {
if(current_suffix.localeAwareCompare(suffix()) == 0)
return;
else
current_suffix = suffix();
}
QString previous_regex = m_validator->regularExpression().pattern();
// remove the ending ")"
previous_regex.chop(1);
QString new_regex = previous_regex +
QStringLiteral("(?:") + suffix() + QStringLiteral(")?)");
m_validator->setRegularExpression(QRegularExpression(new_regex));
});
}
DoubleSpinBoxWithCommasAndPoints::~DoubleSpinBoxWithCommasAndPoints()
{
}
QValidator::State DoubleSpinBoxWithCommasAndPoints::validate(QString &text, int &pos) const
{
return m_validator->validate(text, pos);
}
qreal DoubleSpinBoxWithCommasAndPoints::valueFromText(const QString &text) const
{
QString temp = text;
temp.replace(QStringLiteral(","), QStringLiteral(".")); // replace comma with dot before toDouble()
temp.remove(suffix()); // remove suffix at the end of the value
return temp.toDouble();
}
Few points:
this code works with Qt6
the regular expression accepts negative numbers as well
the regular expression accepts numbers without comma/point
the regular expression supports suffix (code can be adapted to also support prefix)
the regular expression matchs empty string which allows users to clear the whole field
I subclassed QDoubleSpinBox in order to make a QDoubleSpinBox that allows the user to enter NaN as valid input. Right now if a user enters "nan" into the spinbox the control is automatically changing the text to the value of DBL_MAX instead of staying as nan. Before i started using the math.h library with the nan and isnan functions i just defined NAN_VALUE to be 1000 and the range to be -1000 to 1000. Then in my textFromValue i checked if value was equal to NAN_VALUE. Likewise in the valueFromText function i was returning NAN_VALUE. When i did it that way it worked but i would like to be able to use the nan and isnan functions instead. Now that i added in the nan and isnan function calls it stopped working. Does anyone know why that is? Also, i noticed i had this problem with my earlier implementation when i used DBL_MIN and DBL_MAX as the range. Are those numbers too big of a range for the control? If i made the range smaller like -1000 and 1000 it worked fine..
Here is my implementation:
CustomDoubleSpinBox.h
#ifndef CUSTOMDOUBLESPINBOX_H
#define CUSTOMDOUBLESPINBOX_H
#include <QDoubleSpinBox>
#include <QWidget>
#include <QtGui>
#include <iostream>
#include <math.h>
#include <float.h>
#include <limits>
#define NUMBER_OF_DECIMALS 2
using namespace std;
class CustomDoubleSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
CustomDoubleSpinBox(QWidget *parent = 0);
virtual ~CustomDoubleSpinBox() throw() {}
double valueFromText(const QString &text) const;
QString textFromValue(double value) const;
QValidator::State validate ( QString & input, int & pos ) const;
};
#endif // CUSTOMDOUBLESPINBOX_H
CustomDoubleSpinBox.cpp
#include "CustomDoubleSpinBox.h"
CustomDoubleSpinBox::CustomDoubleSpinBox(QWidget *parent) : QDoubleSpinBox(parent)
{
this->setRange(DBL_MIN, DBL_MAX);
this->setDecimals(NUMBER_OF_DECIMALS);
}
QString CustomDoubleSpinBox::textFromValue(double value) const
{
if (isnan(value))
{
return QString::fromStdString("NaN");
}
else
{
QString result;
return result.setNum(value,'f', NUMBER_OF_DECIMALS);
}
}
double CustomDoubleSpinBox::valueFromText(const QString &text) const
{
if (text.toLower() == QString::fromStdString("nan"))
{
return nan("");
}
else
{
return text.toDouble();
}
}
QValidator::State CustomDoubleSpinBox::validate ( QString & input, int & pos ) const
{
Q_UNUSED(input);
Q_UNUSED(pos);
return QValidator::Acceptable;
}