How to refresh QAbstractSpinBox? - qt

I need QSpinBox for unsigned int. Therefore I have wrote simple class:
class UnsignedSpinBox : public QAbstractSpinBox {
private:
uint32_t value = 0;
uint32_t minimum = 0;
uint32_t maximum = 100;
private:
void stepBy(int steps) {
if (steps < 0 && (uint32_t)(-1 * steps) > value - minimum)
value = minimum;
else if (steps > 0 && maximum - value < (uint32_t)steps)
value = maximum;
else
value += steps;
lineEdit()->setText(QString::number(value));
}
StepEnabled stepEnabled() const {
if (value < maximum && value > minimum)
return QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled;
else if (value < maximum)
return QAbstractSpinBox::StepUpEnabled;
else if (value > minimum)
return QAbstractSpinBox::StepDownEnabled;
else
return QAbstractSpinBox::StepNone;
}
QValidator::State validate(QString &input, int &) const {
if (input.isEmpty())
return QValidator::Intermediate;
bool ok = false;
uint32_t validateValue = input.toUInt(&ok);
if (!ok || validateValue > maximum || validateValue < minimum)
return QValidator::Invalid;
else
return QValidator::Acceptable;
}
public:
UnsignedSpinBox(QWidget* parent = 0) : QAbstractSpinBox(parent) {
lineEdit()->setText(QString::number(value));
}
virtual ~UnsignedSpinBox() { }
};
In gerenal it works fine but it has one shortcoming. Step buttons are refreshed only after mouse moving (althouth function stepEnabled is called every second). As a result if I hold Page Up, my spin box gets maximum value and these step buttons don't change their state until I move my mouse. Or if value is 0, one pressing of Page Up or up arrow key on a keyboard changes value and text, but doesn't change buttons' state (down button is still disabled). Moreover, when value == maximum both buttons are disabled although function stepEnabled returnes QAbstractSpinBox::StepDownEnabled (I've checked it). What am I doing wrong? How can I enforce QAbstractSpinBox to draw these buttons correctly?
P.S. I use Debian. But I don't think it does matter since QSpinBox works fine

I think the Qt on your platform is either too old or otherwise broken. It works fine on OS X, on both Qt 4.8.5 and Qt 5.2.0.
There are two other solutions:
If you don't care about full range of unsigned integer, simply use QSpinBox and set non-negative minimum and maximum. That's all. On platforms with 32 bit int, maximum int value is 2^31-1, that's about half of the maximum uint value of 2^32-1.
You can use QDoubleSpinBox. On sane platforms you care about, double has more than 32 bits of mantissa, so you can convert it to a quint32 without loss of precision.
If you want to be sure, just add static_assert(sizeof(double)>4) anywhere in your code.
If one worries about performance, then it really doesn't matter. The calculations are performed at the rate of user input events: that's a couple dozen double operations per second. It doesn't matter.

Related

How can I calculate a moving average in Arduino?

I have to add a moving average to my program which is working now with ultrasonic sensor with Arduino.
First screen of my code
Second screen of my code
The third screen of my code
I hope the following code will help you:
//Parameters
const int aisPin = A0;
const int numReadings = 10;
int readings [numReadings];
int readIndex = 0;
long total = 0;
//Variables
int aisVal = 0;
void setup() {
//Init Serial USB
Serial.begin(9600);
Serial.println(F("Initialize System"));
//Init AnalogSmooth
pinMode(aisPin, INPUT);
}
void loop() {
readAnalogSmooth();
Serial.print(F("ais avg : ")); Serial.println(smooth());
delay(200);
}
void readAnalogSmooth( ) { /* function readAnalogSmooth */
////Test routine for AnalogSmooth
aisVal = analogRead(aisPin);
Serial.print(F("ais val ")); Serial.println(aisVal);
}
long smooth() { /* function smooth */
////Perform average on sensor readings
long average;
// subtract the last reading:
total = total - readings[readIndex];
// read the sensor:
readings[readIndex] = analogRead(aisPin);
// add value to total:
total = total + readings[readIndex];
// handle index
readIndex = readIndex + 1;
if (readIndex >= numReadings) {
readIndex = 0;
}
// calculate the average:
average = total / numReadings;
return average;
}
The basic concept is the same, keep a fixed window size and shift the window after each reading.
Note: Please change the above code according to your need.
There are lots of ways to implement a moving/sliding average. One simple one that I've implemented is below, which works much the same way as HARSH's that he posted in his previous answer. This function, though, is a little more generic and can be used as-is for any data source and you can test it out on any platform. It also handles the startup case when values are first being populated. It is specific, though, to one source. So if you need a moving average for each of multiple sources, you'll have to duplicate the function or modify it to handle multiple sets of data. This uses float values for the data. Even if your data is integer, I'd suggest leaving the averaging data as float. Again, this is a simple averaging where all data values in the window have the same weight.
float movingAverage(float value) {
const byte nvalues = 8; // Moving average window size
static byte current = 0; // Index for current value
static byte cvalues = 0; // Count of values read (<= nvalues)
static float sum = 0; // Rolling sum
static float values[nvalues];
sum += value;
// If the window is full, adjust the sum by deleting the oldest value
if (cvalues == nvalues)
sum -= values[current];
values[current] = value; // Replace the oldest with the latest
if (++current >= nvalues)
current = 0;
if (cvalues < nvalues)
cvalues += 1;
return sum/cvalues;
}
The way you use it is fairly simple. Instead of calling, for example:
x = analogRead(DATA_PIN);
You would call:
x = movingAverage(analogRead(DATA_PIN));
And the movingAverage function does the rest for you. Inside the movingAverage function, you'll see a const value that defines the number of values used in the average. In the above case, it's 8.
You can use movingAverage on any sequence of values, so it doesn't have specific code inside it for reading pins. The way the movingAverage function works is that it keeps track of the last nvalues count of values you call it with and always returns the rolling average of those. It also avoids summing all the values in the window on each call by using a "delta" technique for the sum.

How to do a custom range for a QSpinBox

Hi first time posting here. I searched and found how re-implementing the QSpinBox class allows for custom uses. However I am not sure if my needs are addressed in as much as what I found by re-implementing the validate method.
I need a custom range that excludes a zero value in the range of values. The spinner is used for selecting zoom ratio for a loaded image. The initial range at design time is -25 to 10. That range could change depending on the dimensions of the image. Nevertheless, I have to be able to "skip" zero for a desired zoom factor. For example, the range would have to always be going from -1 to 1 or vice-versa.
I assume you're listening to QSpinbox::valueChanged(int i) signal, there you can do something like this:
void zoomImage(int i) {
if (i == 0) {
if (lastValue < 0) //if sliding from negative values
spinBox->setValue(1);
else
spinBox->setValue(-1);
return; //skip processing for 0
}
else
lastValue = i; //save last state to a class variable
//processing...
}
EDIT: int lastValue is used for storing the position of slider before it hits 0 in order to determine if the user slides to negative or positive values
What seems to have worked:
void MainWindow::zoomImage(int ctlValue)
{
if(ctlValue == 0)
{
if(zoomLastValue < 0)
ui->sbScaleImage->stepBy(1);
else
ui->sbScaleImage->stepBy(-1);
}
zoomLastValue = ui->sbScaleImage->value();
}
Apologies if I screwed up the formatting.

A Boolean recursive function to tell if a digit appears in an integer an even number of times

The function gets an integer and a digit, and should return true
if the digit appears an even number of times in the integer, or false if not.
For example:
If digit=1 and num=1125
the function should return true.
If digit=1 and num=1234
the function should return false.
bool isEven(int num, int dig)
{
bool even;
if (num < 10)
even = false;
else
{
even = isEven(num/10,dig);
This is what I've got so far, and I'm stuck...
This is homework so please don't write the answer but hint me and help me get to it by myself.
To set up recursion, you need to figure out two things:
The base case. What is are the easy cases that you can handle outright? For example, can you handle single-digit numbers easily?
The rule(s) that reduce all other cases towards the base case. For example, can you chop off the last digit and somehow transform the solution for the remaning partial number into the solution for the full number?
I can see from your code that you've made some progress on both of these points. However, both are incomplete. For one thing, you are never using the target digit in your code.
The expression num%10 will give you the last digit of a number, which should help.
Your base case is incorrect because a single digit can have an even number of matches (zero is an even number). Your recursive case also needs work because you need to invert the answer for each match.
This funtion isEven() takes a single integer and returns the true if the number of occurence of numberToCheck is even.
You can change the base as well as the numberToCheck which are defined globally.
#include <iostream>
using std::cout;
using std::endl;
// using 10 due to decimal [change it to respective base]
const int base = 10;
const int numberToCheck = 5;
//Checks if the number of occurence of "numberToCheck" are even or odd
bool isEven(int n)
{
if (n == 0)
return 1;
bool hasNumber = false;
int currentDigit = n % base;
n /= base;
if (currentDigit == numberToCheck)
hasNumber = true;
bool flag = isEven(n);
// XOR GATE
return ((!hasNumber) && (flag) || (hasNumber) && (!flag));
};
int main(void)
{
// This is the input to the funtion IsEven()
int n = 51515;
if (isEven(n))
cout << "Even";
else
cout << "Odd";
return 0;
}
Using XOR Logic to integrate all returns
// XOR GATE
return ((!hasNumber) && (flag) || (hasNumber) && (!flag));

Subclassing Q(Double)SpinBox: reimplement valueFormText() and validate()

Sorry for the vague title.
Currently, if a value is typed into a Q(Double)SpinBox which is out of its range (e.g. typing "100" when max is 90), the value is rejected and instead the last valid value is placed back into the SpinBox.
I want to change this behavior to allow typing out-of-range values which will be automatically corrected (to either the minimum or maximum), because otherwise it would be stupidly hard for the user to guess the value range. After studying the docs and source code of QT, I decided to subclass QSpinBox (will deal with Double variant later) into "QSpinBoxFS", and reimplement both methods mentioned in the title. Somehow though, this is having no effect at all, the behavior is still the same.
These are my methods:
QValidator::State QSpinBoxFS::validate(QString &input,
int &pos)
{
QIntValidator *validator = new QIntValidator();
return validator->validate(input, pos);
}
int QSpinBoxFS::valueFromText(const QString &text)
{
const int max = maximum();
const int min = minimum();
QString copy = text;
int dummy = 0;
QValidator::State state = validate(copy, dummy);
if (state == QValidator::Acceptable)
{
bool ok;
int num = locale().toInt(text, &ok, 10);
if (!ok) { goto bad_text; }
if (num < min) { return min; }
if (num > max) { return max; }
return num;
}
else
{
bad_text:
return (max > 0) ? min : max;
}
}
Of course, this is not really adequate to the pedantic checking done in QSpinBoxPrivate::validateAndInterpret, but I just want the basic concept working for now.
I tried changing validate() to always return Acceptable, but weirdly enough the resulting spinboxes would still behave in the old way.
Either a correction of my own methods or a different approach to this problem are welcome! Thank you for your time.
The signatures of the methods you're trying to reimplement are:
QValidator::State validate(QString & input,int & pos) const # <- const!
int valueFromText(const QString & text) const # <- const!
Both your methods are missing the const, so they are different methods and thus never called from the base class.
On a different note,
QAbstractSpinButton::setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue)
can achieve somehwat similar results (typing values smaller than min will be corrected to min), although you are still prevented from typing values greater than max due to the validator. (And therefor it is insufficient for my needs, just leaving it here for reference.)

QTableView and horizontalHeader()->restoreState()

I can't narrow down this bug, however I seem to have the following problem:
saveState() of a horizontalHeader()
restart app
modify model so that it has one less column
restoreState()
Now, for some reason, the state of the headerview is totally messed up. I cannot show or hide any new columns, nor can I ever get a reasonable state back
I know, this is not very descriptive but I'm hoping others have had this problem before.
For QMainWindow, the save/restoreState takes a version number. QTableView's restoreState() does not, so you need to manage this case yourself.
If you want to restore state even if the model doesn't match, you have these options:
Store the state together with a list of the columns that existed in the model upon save, so you can avoid restoring from the data if the columns don't match, and revert to defualt case
Implement your own save/restoreState functions that handle that case (ugh)
Add a proxy model that has provides bogus/dummy columns for state that is being restored, then remove those columns just afterwards.
I personally never use saveState()/restoreState() in any Qt widget, since they just return a binary blob anyway. I want my config files to be human-readable, with simple types. That also gets rid of these kind of problems.
In addition, QHeaderView has the naughty problem that restoreState() (or equivalents) only ever worked for me when the model has already been set, and then some time. I ended up connecting to the QHeaderView::sectionCountChanged() signal and setting the state in the slot called from it.
Here is the solution I made using Boost Serialization.
It handles new and removed columns, more or less. Works for my use cases.
// Because QHeaderView sucks
struct QHeaderViewState
{
explicit QHeaderViewState(ssci::CustomTreeView const & view):
m_headers(view.header()->count())
{
QHeaderView const & headers(*view.header());
// Stored in *visual index* order
for(int vi = 0; vi < headers.count();++vi)
{
int li = headers.logicalIndex(vi);
HeaderState & header = m_headers[vi];
header.hidden = headers.isSectionHidden(li);
header.size = headers.sectionSize(li);
header.logical_index = li;
header.visual_index = vi;
header.name = view.model()->headerData(li,Qt::Horizontal).toString();
header.view = &view;
}
m_sort_indicator_shown = headers.isSortIndicatorShown();
if(m_sort_indicator_shown)
{
m_sort_indicator_section = headers.sortIndicatorSection();
m_sort_order = headers.sortIndicatorOrder();
}
}
QHeaderViewState(){}
template<typename Archive>
void serialize(Archive & ar, unsigned int)
{
ar & m_headers;
ar & m_sort_indicator_shown;
if(m_sort_indicator_shown)
{
ar & m_sort_indicator_section;
ar & m_sort_order;
}
}
void
restoreState(ssci::CustomTreeView & view) const
{
QHeaderView & headers(*view.header());
const int max_columns = std::min(headers.count(),
static_cast<int>(m_headers.size()));
std::vector<HeaderState> header_state(m_headers);
std::map<QString,HeaderState *> map;
for(std::size_t ii = 0; ii < header_state.size(); ++ii)
map[header_state[ii].name] = &header_state[ii];
// First set all sections to be hidden and update logical
// indexes
for(int li = 0; li < headers.count(); ++li)
{
headers.setSectionHidden(li,true);
std::map<QString,HeaderState *>::iterator it =
map.find(view.model()->headerData(li,Qt::Horizontal).toString());
if(it != map.end())
it->second->logical_index = li;
}
// Now restore
for(int vi = 0; vi < max_columns; ++vi)
{
HeaderState const & header = header_state[vi];
const int li = header.logical_index;
SSCI_ASSERT_BUG(vi == header.visual_index);
headers.setSectionHidden(li,header.hidden);
headers.resizeSection(li,header.size);
headers.moveSection(headers.visualIndex(li),vi);
}
if(m_sort_indicator_shown)
headers.setSortIndicator(m_sort_indicator_section,
m_sort_order);
}
struct HeaderState
{
initialize<bool,false> hidden;
initialize<int,0> size;
initialize<int,0> logical_index;
initialize<int,0> visual_index;
QString name;
CustomTreeView const *view;
HeaderState():view(0){}
template<typename Archive>
void serialize(Archive & ar, unsigned int)
{
ar & hidden & size & logical_index & visual_index & name;
}
};
std::vector<HeaderState> m_headers;
bool m_sort_indicator_shown;
int m_sort_indicator_section;
Qt::SortOrder m_sort_order; // iff m_sort_indicator_shown
};
I would expect it to break if you change the model! Those functions save and restore private class member variables directly without any sanity checks. Try restoring the state and then changing the model.
I'm attempting to fix this issue for Qt 5.6.2, after hitting the same issue. See
this link for a Qt patch under review, which makes restoreState() handle the case where the number of sections (e.g. columns) in the saved state does not match the number of sections in the current view.

Resources