Set specific values in a QSpinBox - qt

I have a widget containing a QSpinBox. This widget also has a QVector<int> Values. What I would like to do is get the QSpinBox to display values issued from Values only.
At first I thought that a new slot and signal in my widget would do the trick, something like
slot :
void ChangeSomeValue()
{
// QVector<int> Values;
// int Index;
int val = Values[ Index ];
emit( SomeValueChanged( val ) );
}
connects :
connect( UI->MySpinBox, SIGNAL( valueChanged(int) ), this, SLOT( ChangeSomeValue() ) );
connect( this, SIGNAL( SomeValueChanged(int ) ), UI->MySpinBox, SLOT( setValue(int) ) );
But then several problems arise :
QSpinBox emit another valueChanged after I call its setValue, resulting in an infinite loop (until my QVector explodes).
I still have to find a way to keep track of Index, depending on which of the QSpinBox arrows was clicked (up or down... I don't even think this is possible).
So my solution, with its problems, seems to be a dead end. Any idea?. I'd like to stick to QSpinBox, if possible.
Thanks !
[EDIT]
Subclassing :
class SpinBox : public QSpinBox
{
Q_OBJECT
public:
explicit SpinBox(const QVector<int> & values, QWidget * parent = 0) :
QSpinBox(parent),
mValues(values),
mIndex(0)
{
qSort(mValues);
setMinimum(mValues.at(0));
setMaximum(mValues.at(mValues.size() - 1));
setValue(mValues.at(0));
}
protected:
void stepBy(int steps) // re-implementaion
{
mIndex += steps;
mIndex = qBound(0, mIndex, mValues.size() - 1);
setValue(mValues.at(mIndex));
}
private:
QVector<int> mValues;
int mIndex;
};

I Would suggest writing your own class to do it by sub-classing QAbstractSpinBox.
Maybe take a look at the accepted answer on this question:
How to subclass QSpinBox so it could have int64 values as maxium and minimum

QSpinBox emit another valueChanged after I call its setValue, resulting in an infinite loop (until my QVector explodes).
You can prevent this by using QObject::blockSignals(). Make sure to unblock signals afterwards.
I still have to find a way to keep track of Index, depending on which of the QSpinBox arrows was clicked (up or down... I don't even think this is possible).
Well, I suppose you can store the old value in a member variable, and when QSpinBox emits valueChanged(), you can compare the new to the old value to figure out whether the up or the down arrow was pressed.
That said, I don't know if that's enough to make QSpinBox behave like you want, correcting the value after it was changed once is a bit hacky. Subclassing QAbstractSpinbox might be better indeed.

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);
}

How to refresh TreeModel in Qt?

I have a TreeModel which has some data loaded in it. There is a radio button in my application, which when clicked should update the TreeModel with a new data.
I have tried the following things on switching the radio button and none is working:
emit layoutChanged
emit layoutChanged();
emit dataChanged
emit dataChanged(QModelIndex(), QModelIndex());
Referenced from:
What does 'bottomRight' mean when using dataChanged() with a QTreeView in Qt?
Recursively visiting each node in the tree and emitting dataChanged
void TreeView::getLastExpandedState(const QModelIndex& parent)
{
bool isExpand = isExpanded(parent);
if (!isExpand) {
return;
}
int rows = model()->rowCount(parent);
for (int rowNum = 0; rowNum < rows ; ++rowNum) {
QModelIndex childIndex = model()->index(rowNum, 0, parent);
model->emitChange(parent,childIndex);
getLastExpandedState(childIndex);
}
}
void TreeModel::emitChange(const QModelIndex& parent,const QModelIndex& childIndex) {
emit dataChanged(parent,childIndex);
}
How to solve that?
The answer you reference is at best confusing and probably wrong.
Your model code needs to emit a dataChanged signal for any rectangle of cells where the data returned by the model data method has changed. See, for example, this question and answer, which goes into some detail.
Note that the parameters in the dataChanged signal should not be parent and child - they should be "topLeft" and "bottomRight". They need to have the same parent and also to be different. So you don't need to iterate over the rows and emit the signal for each row. You can send a combined signal for all the rows that have changed.
But you do need to send a signal for each parent (where data has changed) that signals the top left cell and bottom right cell for that parent. So you might end up creating an index for the top left cell for that parent at (0, 0, parent) and another for the bottom right cell at (rows, cols, parent) and then sending the signal for that pair of indices.

Processing signal of QTreeWidget that has QTreeWidgetItem derivitives as items

I didn't find a proper solution to this problem, so I hope somebody can give me an answer to my problem:
I am using a normal QTreeWidget, but as items I use an own subclass of QTreeWidgetItem (because I needed to store some other information in the item). Now I want to use the itemClicked() signal by the QTreeWidget, but I think my slot doesn't get any signal and I think it has to do with the signature of itemClicked(), since it sends a QTreeWidgetItem and of course not my own subclass.
Is it possible that QTreeWidget doesn't detect a click on my own subclass items?
Here is my connection:
connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *)), this, SLOT(displayTransformData(QTreeWidgetItem*)));
And here is my slot:
void GUI::displayTransformData(QTreeWidgetItem* item) {
cout << "test" endl;
Q_actorTreeWidgetItem* actor_item = dynamic_cast<Q_actorTreeWidgetItem*>(item);
vtkSmartPointer<vtkActor> actor =
vtkSmartPointer<vtkActor>::New();
actor = actor_item->getActorReference();
double* position = new double[3];
position = actor->GetOrigin();
x_loc->setText(QString(std::to_string( position[0]).c_str() ));
}
I'm already trying to cast the item that I could get from the signal into my own subclass, but the slot function is never called, because the test from my cout line doesn't appear in the console.
I'm very grateful for every help!
The problem is your incorrect SIGNAL specification,
SIGNAL(itemClicked(QTreeWidgetItem *))
You should probably see a warning message at the console along the lines of:
QObject::connect: No such signal
tree_widget::itemClicked(QTreeWidgetItem *) in ...
From the documentation the actual signal spec is
void QTreeWidget::itemClicked(QTreeWidgetItem *item, int column)
So, using the old Qt4 syntax you need
connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
this, SLOT(displayTransformData(QTreeWidgetItem*)));
If possible, however, you should make use of the newer Qt5 signal/slot syntax
connect(treeWidget, &QTreeWidget::itemClicked, this, &GUI::displayTransformData);

QEventLoop usage(signals and slots)

I have an application which contains such lines.
emit WindowAdded(settings->WindowType);//!!!!!!!!!!!!!!
MyWindow *widget = new MyWindow(0,settings,currentWindowIndex);
The signal changes value of currentWindowIndex, but it didn't work because of slot, it doesn't change its value in time. Some one advices me to use QEventLoop, but i don't understand how to do this. Give me an example, please.
Another part of the code:
connect(area,SIGNAL(WindowAdded(QString)),this,SLOT(addWindowOnTab(QString)));
void WorkSpace::addWindowOnTab(QString title)
{
qint32 i = TabsList->addTab(title);/////!!!!!!!!!!!!!!!!!!!!!!!!!
emit addedWindowIndex(i);
TabsList->setVisible(true);
}
connect(this,SIGNAL(addedWindowIndex(qint32)),area,SLOT(WindowIndexChanged(qint32)));
void MyMdiArea::WindowIndexChanged(qint32 index)
{
currentWindowIndex=index;
}
I think it can help.
MyMdArea is сlass inherited from QMdiArea, WorkSpace is a QWidget, TabsList is a QTabBar.
And there is another fact: I tried to understand execution sequence of slots and added some lines to code:
QLabel *n= new QLabel("1");
n->show();
after emiting WindowAdded signal
QLabel *n= new QLabel("2");
n->show();
after emiting addedWindowIndex signal
and
QLabel *n= new QLabel("3");
n->show();
after changing currentWindowIndex's value
and that is what i saw "1 2 3" and its exploded my brain. Maybe i don't understand something?

How to make QPushButtons to add text into QLineEdit box?

I used Qt Creator to make a "keyboard" window with sixty QPushButtons and one QLineEdit. How can I make the buttons to add characters into QLineEdit text box? If I press a QPushButton with the label 'Q' on it, I want the program to add the Unicode character 'Q' on the text box.
One way to do this would be to just connect the 'clicked' signal from all the buttons to a slot, and then handle the adding of the character there.
For example, if the all keyboard buttons are inside a layout called 'buttonLayout', in your MainWindow constructor you can do this:
for (int i = 0; i < ui->buttonLayout->count(); ++i)
{
QWidget* widget = ui->buttonLayout->itemAt( i )->widget();
QPushButton* button = qobject_cast<QPushButton*>( widget );
if ( button )
{
connect( button, SIGNAL(clicked()), this, SLOT(keyboardButtonPressed()) );
}
}
Then in the slot implementation, you can use QObject::sender(), which returns the object that sent the signal:
void MainWindow::keyboardButtonPressed()
{
QPushButton* button = qobject_cast<QPushButton*>( sender() );
if ( button )
{
ui->lineEdit->insert( button->text() );
}
}
OPTION 1 - Multiple signals and slots
Connect all pushbuttons clicked() signal to a slot
// Let button01 be the A
connect(ui->button01, SIGNAL(clicked()), this, SLOT(buttonClicked()));
...
// Let button 60 be the Q
connect(ui->button60, SIGNAL(clicked()), this, SLOT(buttonClicked()));
In the buttonClicked() slot you have to figure out which button was clicked and append the corresponding letter to the line edit.
void buttonClicked()
{
QObject* callingButton = QObject::sender();
if (callingButton == button01)
ui->lineEdit->setText(ui->lineEdit->text()+ "A");
...
else if (callingButton == button60)
ui->lineEdit->setText(ui->lineEdit->text()+ "Q");
}
OPTION 2 - Subclass QPushButton
You could subclass QPushButton in order to avoid the multiple ifs. In your subclass just catch the mouse release event and emit a new signal which will contain the button's text
void KeyboardButton::mouseReleaseEvent(QMouseEvent* event)
{
emit clicked(buttonLetter); // Where button letter a variable of every item of your subclass
}
Similarly connect the clicked(QString) signal with a slot
connect(ui->button01, SIGNAL(clicked(QString)), this, SLOT(buttonClicked(QString)));
...
connect(ui->button60, SIGNAL(clicked(QString)), this, SLOT(buttonClicked(QString)));
void buttonClicked(QString t)
{
ui->lineEdit->setText(ui->lineEdit->text()+ t);
}
I have created an application with a similar issue, trying to convert the qpushbutton text to the qlineedit itself. The key is how you initialize the buttons and to use polymorphism in your function. To create an emit signal wont work for individual characters. The .digitValue will work if the case if for numerics (which the buttons would be of type int), but qt doesnt have a character value (I should say after 6hrs of reading qt doc and another 4 of trying different combinations it would not work), I think it has to do with how many bits it takes to store each variable type in an array. I even tried converting the button->text to QString to use with the emit function as a signal prototyped.
I do not know what your button layout is, but I will give you a synopsis of what I did. I first created a global, static const char array containing all the letters needed e.g.
static const char vowelarray[] = "AEIOU";
Then initialized the QPushButtons with in the MainWindow function, using iteration, setting a for loop's terminating condition equal to the size char array (in your case 60?). This all depends on your button layout though. I personally created a void function (setLocation) for the button->setGeometry of each button and iterated the setGeometry, and then passed the function to the MainWindow Function, at end of fucntion. The following code was used to initialize the buttons, connect signals to slots, and use polymorphism to connect to lineedit.
for (int i = 0; i < 26; i++){
characterButton[i] = new QPushButton(chararry[i], this); `
characterButton[i] -> setStyleSheet("QPushButton{background: grey; color: brown}");
connect(characterButton[i],SIGNAL(released(),this,SLOT(characterPushed()));
}
setLocation();
Then created a void function (e.g. void MainWindow::characterPuched()) where the following code was used:
void MainWindow::characterPushed(){
QPushButton *characterButton = (QPushButton*) sender();
if (characterButton )
{
lineEdit -> setText(letters.insert(letters.size(), characterButton -> text()));
}
lineEdit -> setText(letters);
}
of course letters was a global variable as well as:
QString letters = "";
and of course the QPushButtons and the function were prototype in the header file as a private variables and slots, e.g.
private:
QPushButton *characterButton[26];
the variable 'letters' was used to extract and input text to and from the line edit for further functions throughout the application.
Best Luck!!``

Resources