How to display fixed width values in doublespinbox - qt

I have 4 doublespinboxes in my Window. I want to display the values of fixed width in this doublespinbox. For e.g my doublespinbox range is 0 to 100.00 I want to display the values in the format of 000.00 always.So though the value is 8 it should get displayed as 008.00 in my doublespinbox . Similarly I want highlight each digit in my doublespinbox during editing the values . How can I do the same? . The width/range varies for all the spinboxes. Can somebody help me.

As I've said in comment to #asclepix post you need to reimplement textFromValue. This snippet works fine for me.
class MyDoubleSpinBox : public QDoubleSpinBox
{
public:
explicit MyDoubleSpinBox(QWidget *parent = 0) : QDoubleSpinBox(parent) {
setMaximum(999.99);
}
QString textFromValue(double val) const {
const int width = 6; // length of whole number in symbols '000.00'
const int precision = 2; // after separator
// rightJustified to add leading zeroes
return QLocale().toString(val, 'f', precision).rightJustified(width, '0');
}
};

I suppose you have to use setDecimals to to force the trailing zeros and setPrefix for the leading ones. The problem is that you have to change the prefix depending on the value of the doublespinbox. The simple way is to connect a slot to the valueChanged signal and do the job there. The less simple way is to subclass the doublespinBox, but I don't know what to reimplement.

Related

QTextLayout / QTextLine support grouping of several-characters so it acts as one character for the cursor

Is it possible for QTextLayout to render several characters, but to process/handle it as one character. For example rendering a code point like: [U+202e], and when moving the caret/calculating positions, it is treated as one character.
Edited:
Please check this following issue, were I explain what I'm trying to do. It for the edbee Qt component. It's using QTextLayout for line rendering.
https://github.com/edbee/edbee-lib/issues/127
Possibly it isn't possible with QTextLayout, the documentation is quite limited.
According to Qt docs:
"The class has a rather low level API and unless you intend to implement your own text rendering for some specialized widget, you probably won't need to use it directly." - https://doc.qt.io/qt-5/qtextlayout.html#details
You should probably use a QLineEdit or a QTextEdit (each has a method called setReadOnly(bool)).
Before answering the question, I will point out that the CursorMode enum (https://doc.qt.io/qt-5/qtextlayout.html#CursorMode-enum) seems very promising for this problem, but to me, the documentation isn't clear on how to use it or set it.
Now to answer your question in regards to QLineEdit or QTextEdit, it's a bit complicated, but it's the same for QLineEdit and QTextEdit, so lets look at QTextEdit.
Firstly, mouse clicks: QTextEdit has a signal called cursorPositionChanged(), which will be helpful here. You'll want to connect that to a custom slot, which can make use of the function moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor) (https://doc.qt.io/qt-5/qtextedit.html#moveCursor). Notice that there are very helpful enumeration values for you here in QTextCursor::MoveOperation regarding word hopping (https://doc.qt.io/qt-5/qtextcursor.html#MoveOperation-enum). How do we put all of this together? Well, probably the right way to do it is to determine the width of the chars to the left of the cursor's position and the width of the chars to the right of the cursor's position when the cursorPositionChanged() signal is emitted and go to the side of the word that has less width. However, I'm not sure how to do that. At this point I'd settle with checking the number of chars to the left and right and going to the side with less.
Secondly, keyboard presses: This goes a bit out of my knowledge, but almost everything drawable and iteractable inherits from QWidget. Take a look at https://doc.qt.io/qt-5/qwidget.html#keyPressEvent and it's possible that overriding that in your own implementation of QTextEdit is necessary to get the left arrow and right arrow keypresses to jump words (once you get that part it's pretty easy, just use the same function as last section for moving the cursor, or in the case of QLineEdit, cursorWordForward()/cursorWordBackward()).
All this being said, I've so far been assuming that you're not deleting anything or selecting anything. Selection can be a real pain depending on if you allow multiple selections, but the functions are all there in the documentation to implement those things.
Example of mouse click impl:
myclass.hpp
#include <QTextEdit>
#include <QTextCursor>
#include <QObject>
#include <QString>
int distance_to_word_beginning_or_end(const QString &str, int index, bool beginning);
class MyClass {
MyClass();
~MyClass();
private:
QTextEdit *text_edit;
public slots:
void text_edit_changed_cursor_location();
};
myclass.cpp
#include "myclass.hpp"
int distance_to_word_beginning_or_end(const QString &str, int index, bool beginning)
{
// return the distance from the beginning or end of the word from the index given
int inc_or_dec = (beginning) ? -1 : 1;
int distance = 0;
while (index >= 0 && index < str.length())
{
if (str.at(index) == ' ' || str.at(index) == '\n' || str.at(index) == '\t')
{
return distance;
}
distance++;
index += inc_or_dec;
}
return --distance;
}
MyClass::MyClass()
{
text_edit = new QTextEdit();
QObject::connect(text_edit, &QTextEdit::cursorPositionChanged, this, &MyClass::text_edit_changed_cursor_location);
}
MyClass::~MyClass()
{
delete text_edit;
}
void MyClass::text_edit_changed_cursor_location()
{
QString text_edit_string = text_edit->text();
QTextCursor text_edit_cursor = text_edit->textCursor();
auto current_position = text_edit_cursor.position();
QTextCursor new_text_cursor;
int distance_to_beginning = distance_to_word_beginning_or_end(text_edit_string, current_position, true);
int distance_to_end = distance_to_word_beginning_or_end(text_edit_string, current_position, false);
auto movement_type;
if (distance_to_beginning > distance_to_end)
{
new_text_cursor.setPosition(current_position + distance_to_end);
} else {
new_text_cursor.setPosition(current_position - distance_to_beginning);
}
text_edit->setTextCursor(new_text_cursor);
}

QT QtableWidgetItem display formated data and sort by original data

In a column in a QTableWidget I want to display some double values. By doing the following, I get my desired display:
double value = 1234.567;
QTableWidgetItem* qti = new QTableWidgetItem(QString::number(value , 'f', 4));
Now, if I enable sorting on the table and sort the values in this column, it will sort as strings. So 90.0000 will come be "larger" than 800.0000 for example (9 > 8).
If I do this instead:
QTableWidgetItem* qti = new QTableWidgetItem();
qti->setData(Qt::DisplayRole, value);
or
QTableWidgetItem* qti = new QTableWidgetItem(QString::number(value , 'f', 4));
qti->setData(Qt::DisplayRole, value);
I can sort my column correctly, but I "lose" the formmating (12.0000 is displayed as 12).
I've also tried like this:
QTableWidgetItem* qti = new QTableWidgetItem();
qti->setData(Qt::UserRole, value);
qti->setData(Qt::DisplayRole, QString::number(value, 'f', 4));
How can I format the display of the values, while still enabling sorting?
(In all code snippets above, the QTableWidgetItems' are added by:
table->setItem(rowNumber, colNumber, qti);
where table is a QTableWidget)
You do it wrong. Let's do it right. The solution is to subclass QTableWidgetItem.
class CustomTableWidgetItem : public QTableWidgetItem
{
public:
CustomTableWidgetItem(const QString txt = QString("0"))
:QTableWidgetItem(txt)
{
}
bool operator <(const QTableWidgetItem& other) const
{
qDebug() << "Sorting numbers";
return text().toFloat() < other.text().toFloat();
// text() is part of QTableWidgetItem, so you can write it as QTableWidgetItem::text().toFloat() as well
}
};
Example insertions
ui->tableWidget->setSortingEnabled(false);
ui->tableWidget->insertRow(0);
ui->tableWidget->setItem(0, 0, new CustomTableWidgetItem(QString::number(90.0005, 'f', 4)));
ui->tableWidget->insertRow(1);
ui->tableWidget->setItem(1, 0, new CustomTableWidgetItem(QString::number(800.0003, 'f', 4)));
ui->tableWidget->insertRow(2);
ui->tableWidget->setItem(2, 0, new CustomTableWidgetItem(QString::number(200.0010, 'f', 4)));
ui->tableWidget->insertRow(3);
ui->tableWidget->setItem(3, 0, new CustomTableWidgetItem(QString::number(200.0020, 'f', 4)));
ui->tableWidget->setSortingEnabled(true);
Sorting behaviour is as expected
ascending
90.0005
200.0010
200.0020
800.0003
descending
800.0003
200.0020
200.0010
90.0005
PS: Remember to turn off sorting before you insert bew items.
Another way of doing this is by using QStyledItemDelegate attached to the double column(s). It need not do much except implement the QStyledItemDelegate::displayText() function.
For further details, see QTableView and display of doubles at Qt Centre.
Here's what I came up with, based on Mangesh's answer, for a similar problem displaying a percentage:
class percentageItemC : public QStyledItemDelegate
{
QString displayText(const QVariant &value, const QLocale &locale) const
{
return QString::asprintf("%0.3lf%% ", value.toDouble());
}
} percentageItem;
That's any number of digits to the left of the decimal, 3 to the right, and a long float with a percent sign. Then, do a
ui->twMyTable->setItemDelegateForColumn(1, &percentageItem);
in yer form's class. I can also re-use them for other columns, and presumably rows.
This method doesn't slow things down with extra text to float conversions. You can also have nice extras, like commas, percent signs and currency signs in it. I have this for currency:
class currencyItemC : public QStyledItemDelegate
{
QString displayText(const QVariant &value, const QLocale &locale) const
{
return locale.toCurrencyString(value.toDouble()) + " ";
}
} currencyItem;
I use:
QTableWidgetItem *item = new QTableWidgetItem();
item->setData(Qt::EditRole, number);
to create the table items. It is smart about these: it creates a QVariant, and then sorts based on the type. This way, you don't have to set the underlying data to a string, but it can be any type it has a sort for.
The columns sort numerically like they're s'posed to, but they display in any format ya like. ;}
From my point of view the best option to solve this problem is to create new class with usage of setData() with Qt::UserRole to store unformatted data. In this setup you can store real data and text together. This has more memory consumption but you doesn't need to convert from string to int/float/etc.
I've tested many other roles Qt::EditRole, Qt::DisplayRole, etc and they are overwriting stored data/text and you can't store both values.
Here is my final solution of class displaying % percent values (python code)
from PyQt5 import QtWidgets, QtCore
class PercentTableWidgetItem(QtWidgets.QTableWidgetItem):
# Float value
value: float = 0
def __init__(self,
value: float = 0
):
''' Constructor.'''
super().__init__()
# Set variables
self.value = value
# Set item data
self.setData(QtCore.Qt.UserRole, value)
self.setText(f'{self.value:.2f}%')
def __lt__(self, other: QtWidgets.QTableWidgetItem):
''' Operation < for sorting.'''
value = other.data(QtCore.Qt.UserRole)
return (value is not None) and (self.value < value)
Usage in code as simple as below
table = QTableWidget()
table.setItem(0,0,PercentTableWidgetItem(35.67))

ARDUINO: I can't manage to use a variable as index for my array

int sum = 3;
int i = 0;
myCode[] = {};
void loop () {
myCode[i] = sum;
}
In this example the variable 'i' gets the value of '3' instead of assigning the value of '3' to the array myCode[] with the index '0' (i).
I honestly don't know why it does this. This is only a small part of the program. I don't include the full program because that would only be confusing for you guys and this is the only part of the program that is not working.
How would I use the value of i to assign a value to the array with i as index??
It looks like you see undefined behavior, because myCode array does not have enough space. Since you see myCode[0] modifying the value of i instead of myCode, the size of myCode in your compiled program is zero, and its address is the same as i's address.
Fixing this problem requires allocating myCode at the max size that you expect it to have:
int myCode[20] = {0};
Now the code is going to work correctly for indexes between 0 and 19, inclusive.

How to sort data in QTableWidget?

I have a QTableWidget and the first column contains numbers from 1 to 1000. Now I need to sort the table based on this first column.
I'm using the function sortItems(int column, Qt::AscendingOrder), but it is displayed as:
1, 10, 100, 1000, 101, 102, ...
Yet I need this result:
1, 2, 3 ,4...., 1000.
I'm using a CSV file for populating the table.
The values are sorted as strings because you stored them as such in the model.
The QVariant can remember the original type of the data if you let it do the conversion itself, and the comparison operator from that type will be used when sorting:
// Get the value from the CSV file as a numeric type
int valueFromCsvFile = ...;
// don't do this
QTableWidgetItem *item = new QTableWidgetItem(QString::number(valueFromCsvFile));
// but do this instead
QTableWidgetItem *item = new QTableWidgetItem;
item.setData(Qt::EditRole, valueFromCsvFile);
The cell editor will also adapt to the type of the QVariant:
QSpinBox for int,
QDoubleSpinBox for double and float,
QDateTimeEdit for QDateTime
...
The easiest way is probably to subclass QTableWidgetItem and then implement the < operator to be smart about the fact that you're sorting numbers and not strings.
class MyTableWidgetItem : public QTableWidgetItem {
public:
bool operator <(const QTableWidgetItem &other) const
{
return text().toInt() < other.text().toInt();
}
};
Then when you're populating your table you can pass it instances of your custom items that know how to sort themselves properly instead of the generic ones.
One way that worked in my situation was
1) before filling the table, turn off sorting:
table.setSortingEnabled(False)
2) pad the number strings with blanks and make all strings in the column have the same length:
(' '+numStr)[-4:]
3) after filling the table, turn on sorting:
table.setSortingEnabled(True)
This fixed the row sorting problem and the numerical order.
I don't know if the accepted answer used to work, but with Qt5.1, it doesn't.
In order to work, the operator< definition has to match the virtual definition from qtablewidget.h.
Another interesting addition is to sort items that have numbers, but start with a currency sign ($ or € for instance) or end with %.
Here is the updated code:
class TableNumberItem : public QTableWidgetItem
{
public:
TableNumberItem(const QString txt = QString("0"))
:QTableWidgetItem(txt)
{
}
bool operator < (const QTableWidgetItem &other) const
{
QString str1 = text();
QString str2 = other.text();
if (str1[0] == '$' || str1[0] == '€') {
str1.remove(0, 1);
str2.remove(0, 1); // we assume both items have the same format
}
if (str1[str1.length() - 1] == '%') {
str1.chop(1);
str2.chop(1); // this works for "N%" and for "N %" formatted strings
}
double f1 = str1.toDouble();
double f2 = str2.toDouble();
return str1.toDouble() < str2.toDouble();
}
};
Then, you add the items that contain numbers using something like this:
myTableWidget->setItem(row, col, new TableNumberItem("$0"));
Note that this class must be used with numbers only, it will not sort strings correctly (as is also the case with the accepted answer).
I had same problem and the The answer of #Chris worked for me!
but a little modification is need. I can't comment. so I write here.
class MyTableWidgetItem : public QTableWidgetItem {
public:
bool operator <(const QTableWidgetItem &other) const
{
if (text()=="")
return text().toDouble() > other.text().toDouble();
else
return text().toDouble() < other.text().toDouble();
}
};

How to set font Bold to a particular row in table widget

i want to set my font as bold in particular row column position of my tablewidget.
I did like this but getting break.
QFont font("Helvetica", 12, QFont::Bold);
overviewTable->item(2,2)->setFont(font);
Please Help
I think everything is ok. Here what docs said:
void QTableWidgetItem::setFont ( const QFont & font )
Sets the font used to display the item's text to the given font.
Maybe your overviewTable const?
ADDED:
This variant works fine for my Qt 4.6:
tableWidget = new QTableWidget(12, 3, this);
for (int i = 0; i < 12; i++) {
for (int j = 0; j < 3; j++) {
QTableWidgetItem *newItem = new QTableWidgetItem(tr("%1").arg(
(i+1)*(j+1)));
tableWidget->setItem(i, j, newItem);
}
}
QFont font;
font.setBold(true);
tableWidget->item(2, 2)->setFont(font);
Maybe you are getting break because you didn't call setItem() to set an item for the cell (2, 2) before you use overviewTable->item(2,2). As the Qt document says,
QTableWidgetItem * QTableWidget::item(int row, int column) const
Returns the item for the given row and column if one has been set;
otherwise returns 0.
That is, your overviewTable->item(2,2) probably returns 0, thus causes a Segmentation fault in the setFont() call.
So your means to setting font is completely right. You just need to call setItem() at first as mosg's answer suggests.
ADDED:
if your overviewTable is a QTableWidget created in Qt Designer, then in the Designer a double-click on a cell (just to enter its editing mode, no need to actually enter anything) will have the effect of calling setItem() for that cell. Later in your code you can directly using the item() function without having to call setItem() first.

Resources