How to have QLabel update as various numbered pushbuttons are clicked - c++

I have a dialpad with numbers 1-9 and 0, and a QLabel above it to show the numbers when clicked(same as a keypad on any phone). All are push buttons. What is the easiest way to get the QLabel to show the numbers as the push buttons are clicked?
For example, if 2 then 0 then 7 is clicked, the label would update in real time with 207. The format of the Qlabel should follow standard phone numbers, 000-000-0000. I understand how to setText for one number at a time, but they keep overriding each other.
Any help is appreciated.
Thank you in advance

What you are looking for is a QSignalMapper. It maps multiple inputs through a single interface and does the sender dispatching for you.
QSignalMapper *mapper(new QSignalMapper(parent));
for (int i=0; i<10; ++i){
QPushButton *button = some_new_button_function();
connect(button, &QPushButton::clicked, mapper, &QSignalMapper::map);
mapper->setMapping(button, i);
}
connect(mapper, QOverload<int>::of(&QSignalMapper::mapped),
[this](int i){/*here your append code*/});

The easiest is to connect the clicked signal of the buttons to a slot (possibly a lambda) that changes the text of the QLabel (using setText()). If you want to append to the current text, then just do setText(label.text() + "new text");.

You have to connect the signals clicked() emitted by each QPushButton to a slot that update the QLabel text.
A brief example
In the parent constructor:
connect(qpb1, &QPushButton::clicked, this, &MyClass::handleQLabel);
And the possible slot implementation:
void MyClass::handleQLabel()
{
QPushButton * qpb = qobject_cast<QPushButton*>(sender()); // Find the sender of the signal
if(qpb != nullptr)
this->myLabel->setText(qpb->text()); // Write anything you want in the QLabel
else
{
// Do what you want.
}
}
This will do the job.
Of course if you don't want use sender() (for multi-threading concerns for example) you can either create one slot by QPushButton and do the same number of connect (heavy and quite a dirty workaround), or create a subclass of QPushButton to add a custom signal to emit with an identifier of the QPushButton and get it with a slot for example.
I hope it can help :)

QLineEdit might better suit your needs in this case if you also want your data representation to follow phone number standard such as "000-000-0000". You can make it read-only, disable interaction flags if you like (but from UI/UX perspective it is better not to, since mostly there is no reason to disallow copying), and also you can set input mask you like. Given your current situation, you can base your needs on the following example:
// Set your format.
ui->lineEdit->setInputMask("000-000-0000");
// Make sure that your text would be in the format you like initially.
ui->lineEdit->setText("999-999-9999");
// Text will be not editable.
ui->lineEdit->setReadOnly(true);
// And here, you can use QSignalMapper as other members have suggested. Or you can just connect multiple buttons somehow. The choice is yours to make.
connect(ui->pushButton, &QPushButton::clicked, ui->lineEdit, [this]
{
// Just keep in mind taht when returning text, some of the mask elements might be here, too.
ui->lineEdit->setText(ui->lineEdit->text().replace("-", "") + "1");
});

Related

QT Signals & slots in diffrent class

My program has 2 classes. One of them is MainWindow and another is Calc.
In main window I use automatic generated function on_PushButton_clicked. This function should send two values: double & char to function in Calc.
first:
void MainWindow::on_OneButton_clicked(){
QObject::connect(ui->ZeroButton , SIGNAL(clicked()), this, SLOT(...)) );
ui->TextEdit->insertPlainText("1");
}
second :
void Calc::Add(double val, char oper){
//compute something
}
It's my first app with Qt and I do not know how can I connect them. I've searched similar question on this forum, but can't found.
Sorry if i'm wrong.
First of all, you have to well understand what signal/slot mecanism is, and what you are doing.
Signal/slot mecanism is a Qt concept to link a function (signal) to another function (slot). To "make a link" between a signal and a slot, you have to connect them using QObject::connect(...).
When you use automatic generated function on_PushButton_Clicked() with Qt designer, you, in fact, "make a link" between the signal clicked() emitted when the pushButton is clicked, with a slot on_PushButton_Clicked(). However, the connection between this signal and this slot doesn't appear in your code so it may be confusing and that's why I'm pointing it out.
When you write this:
void MainWindow::on_OneButton_clicked(){
QObject::connect(ui->ZeroButton , SIGNAL(clicked()), this, SLOT(...)) );
ui->TextEdit->insertPlainText("1");
}
You create a connection with zeroButton when clicked and a slot, each time you clic on your button. As a connection is valid till an object is destructed, if you clic again on your pushButton, you'll have a second connection between zeroButton when clicked and your slot.
A better way to create connection is to use connect(...) function when you create your object (mainWindow in your case).
To make it simple for your calculator, you can create 9 buttons for digits, 4 buttons for operators, and 1 button to compute everything.
In your mainwindow constructor, you could have something like:
connect(ui->pushButton1, SIGNAL(clicked()), this, SLOT(onPushButton1Clicked()));
.... // Every other signal for each button
connect(ui->pushButtonEqual, SIGNAL(clicked(), this, SLOT(onPushButtonEqualClicked());
And in your body
void MainWindow::onPushButton1Clicked()
{
// concatenate current value + 1
ui->textEdit->insertPlainText(ui->textEdit->toPlainText() + "1");
}
void MainWindow::onPushButtonEqualClicked()
{
// check textedit content (need a digit + operator + digit)
...
// compute result
...
// write result in TextEdit
...
}
I hope it will help a little bit ;)

QLineEdit editingFinished signal twice when changing focus?

I've found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler. In my case I am a bit stuck as I am getting the editFinished signal twice even when my slot handler is doing nothing.
For a test, I have an array of QLineEdit which use a signalMapper to connect the editingFinished() signals to a single slot. The signalMapper passes the array index so I can see where the signal came from.
eg:
testenter::testenter(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::testenter)
{
// setup the UI according to the .h file
ui->setupUi(this);
signalMapper = new QSignalMapper(this);
// init the labels and edit boxes
for (int i = 0; i < 10; i++)
{
pm_label[i] = new QLabel(ui->scrollArea);
QString text = QString("Number %1").arg(i);
pm_label[i]->setText(text);
pm_label[i]->setGeometry(10,20+i*30, 50, 20);
pm_label[i]->show();
pm_editBox[i] = new QLineEdit(ui->scrollArea);
pm_editBox[i]->setGeometry(80,20+i*30, 50, 20);
pm_editBox[i]->show();
signalMapper->setMapping(pm_editBox[i], int(i));
connect(pm_editBox[i], SIGNAL(editingFinished()), signalMapper, SLOT(map()));
}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(data_entry(int)));
}
void testenter::data_entry(int entry)
{
//dummy
}
When run in the debugger, if I enter data into one box then either hit return or select another box with the mouse (ie change focus) , then it calls data_entry twice, the first time with index of the box that is losing focus and the 2nd time with the box which gets the focus.
So my question: Am I missing something? Is this expected behaviour or a bug?
If a bug, anyone know a way round it as I wanted to use this signal to do custom validation on data when it is entered (by either return, tab or mouse click to change focus).
First off, no this isn't expected behavior, i.e. selecting a QLineEdit should not cause it's editingFinished signal to be emitted.
There are a couple of possible things that may cause this problem:
You've accidentally connected a signal twice to a slot
The slot map() is causing the newly selected box to lose focus
In the same vain, if you're debugging and using a break point to detect when the slots are getting called you may be causing the QLineEdit to lose focus when the active application changes from your QWidget to your debugger, again causing the signal to be sent again.
If you're having problems because of a doubly connected slot, which doesn't seem to be the case because you're specifically getting a signal from two different QLineEdits, you can make sure that this isn't happening by specifying the connection type, the connect method actually has an additional optional argument at the end which allows you to change the type from a DefaultConnection to a UniqueConnection.
That being said, data validation is something that Qt has an established mechanism for, and I suggest that you use it if possible, look into extending the QValidator abstract base class Ref Doc. You then tell each of your QLineEdit's to use the same validator.
I have run into the same issue. It really does emit the signal twice, which is a known bug: https://bugreports.qt.io/browse/QTBUG-40 which however has not been addressed for a very long time.
Finally I found that the best solution in my case is to change the signal from editingFinished to returnPressed. As a side effect this behaves much more predictably from the user perspective. See also here: http://www.qtforum.org/article/33631/qlineedit-the-signal-editingfinished-is-emitted-twice.html?s=35f85b5f8ea45c828c73b2619f5750ba9c686190#post109943
The OP "found a few similar questions on this but these appear to refer to cases where a message box is used in the slot handler." Well, that is my situation also, and here is where I ended up. So, at the risk of going off topic...
In my situation, when my slot receives the editingFinished signal sent from the QLineEdit, I launch a modal QMessageBox to ask the user something. The appearance of that message box is what triggers the QLineEdit to send the second, undesirable editingFinished signal.
A post in the bug report (https://bugreports.qt.io/browse/QTBUG-40) mentioned by #V.K. offers a workaround which helped me. The following is my implementation of the workaround. I let Qt magic mojo automatically connect the QLineEdit signal to my MainWindow slot.
void MainWindow::on_textbox_editingFinished( void )
{
QLineEdit * pTextbox = qobject_cast<QLineEdit *>( QObject::sender() );
if ( !pTextbox->isModified() )
{
// Ignore undesirable signals.
return;
}
pTextbox->setModified( false );
// Do something with the text.
doSomething( pTextbox->text() );
}
void MainWindow::doSomething( QString const & text )
{
QMessageBox box( this );
box.setStandardButtons( QMessageBox::Yes | QMessageBox::No );
box.setText( "Are you sure you want to change that text value?" );
if ( box.exec() == QMessageBox::Yes )
{
// Store the text.
m_text = text;
}
}

hide and show lineEdit with QComboBox in Qt

I need to create application which will work with database and I need to connect QCombobox with lineEdits(1,2,3). I have QComboBox with two items(1,2) and I want to hide lineEdits(2,3) when I change to item 2. And when I change back to item 1 I want to show lineEdits(2,3) and hide lineEdit 1. Can somebody help me? :-) Thanks
You simply connect the currentIndexChanged(int index) signal of your QComboxBox to a slot in your class and hide/show the QLineEdit's depending on the the value of index.
QObject::connect(myComboxBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(onComboxBoxCurrentIndexChanged(int)));
The slot can be implemented as follows:
MyClass::onComboxBoxCurrentIndexChanged(int index)
{
if(index == 0){
lineEdit1->setVisible(false);
lineEdit2->setVisible(true);
lineEdit3->setVisible(true);
}
else{
lineEdit1->setVisible(true);
lineEdit2->setVisible(false);
lineEdit3->setVisible(false);
}
}
Note that your class must be a subclass of QObject for the slot to work. If you don't know how signal and slots work I would recommend reading a tutorial first.
See http://qt-project.org/doc/qt-4.8/signalsandslots.html

How to issue signal each time a row is edited in QListWidget?

class genericTaskList : public QListWidget
{
Q_OBJECT
public:
QListWidgetItem *defaultText;
genericTaskList (QWidget *parentWidget)
{
setParent (parentWidget);
setFixedSize (445, 445);
defaultText = new QListWidgetItem ("Double click here to compose the task");
defaultText->setFlags (defaultText->flags () | Qt :: ItemIsEditable);
insertItem (0, defaultText);
QObject :: connect (this, SIGNAL (currentRowChanged (int)), this, SLOT (addDefaultText (int)));
}
public slots:
void addDefaultText (int rr)
{
std::cout << "\ndsklfjsdklfhsdklhfkjsdf\n";
insertItem (++rr, defaultText);
}
};
This code is supposed to issue a signal each time the row gets edited.
After I call "insertItem" in the constructor, the signal is issued.
But, that's it. It never gets issued after that - no matter how many times I edit the row.
What am I missing?
At first it seems like QListWidget::itemChanged is the way to go, but soon you run into a problem: the signal is sent for everything - inserts, removes, changing colors, checking boxes, etc! So then you end up trying to put in flags and filter everywhere by intercepting various signals to find out if editing was the actual event. It gets very messy.
There is also QAbstractItemModel::dataChanged , which would seem like a good solution. It even has a parameter "const QVector& lstRoles" so you could scan for Qt::EditRole and see if it was really edited. Alas, there's a catch - it gets called for everything just like QListWidget::itemChanged and unfortunately, for QListWidget anyway, the roles parameter is always empty when it's called (I tried it). So much for that idea...
Fortunately, there's still hope... This solution does the trick! :
http://falsinsoft.blogspot.com/2013/11/qlistwidget-and-item-edit-event.html
He uses QAbstractItemDelegate::closeEditor, but I prefer using QAbstractItemDelegate::commitData.
So make a connect like so...
connect(ui.pLstItems->itemDelegate(), &QAbstractItemDelegate::commitData, this, &MyWidget::OnLstItemsCommitData);
Then implement the slot like this...
void MyWidget::OnLstItemsCommitData(QWidget* pLineEdit)
{
QString strNewText = reinterpret_cast<QLineEdit*>(pLineEdit)->text();
int nRow = ui.pLstItems->currentRow();
// do whatever you need here....
}
Now you have a slot that gets called only when the list item's text has been edited!
currentRowChanged indicates the row selection has changed, not the content of the row. Perhaps you want to use currentTextChanged or itemChanged instead.
The reuse of the word current and changed in the QT docs is quite confusing.
Warning: A QListWidgetItem can only be added to a QListWidget once. Adding the same QListWidgetItem multiple times to a QListWidget will result in undefined behavior.
So even if it will emit the signal I think you should better to add newly created Item.
And when do you want the new row to be inserted ? -
as soon as item is double clicked or finishing edit - they differ.

Fewer connections in a Qt calculator

I'm writing a simplified calculator using Qt with C++, for learning purposes. Each number is a QPushButton that uses the same slot to modify the text in a lineEdit widget being used as a display.
The slot uses the sender() method to figure out which button was pressed, so the correct number would be written on the display widget.
In order to have all the buttons working, I'd have to write a connection to each one of them, kinda like this:
connect(ui->button1, SIGNAL(clicked()), this, SLOT(writeNum()));
Since they all use the same slot, the only thing that changes is the button being used, so the next sender would be ui->button2, ui->button3, and so on. My question is, is there a way to reduce the number of defined connections?
Edit: Here's a useful link discussing precisely about this problem, in detail.
If you use QtDesigner or the form editor of QtCreator you can just drag lines between the 2 and it will fill in the code for you.
You could also keep all the buttons in a list structure, but I would use a QVector not a standard array.
You might also want to reconsider using the sender() method, it violates OOP design. Instead connect all the buttons to a QSignalMapper and then connect mapped() to your text box.
You should use an int in this case to identify the button which sent the signal to your slot. Essentially you use QSignalMapper for that task:
QSignalMapper sm;
QPushButton* one = new QPushButton(this);
QPushButton* two = new QPushButton(this);
QPushButton* three = new QPushButton(this);
//and so on...
sm.setMapping(one, 1);
sm.setMapping(two, 2);
sm.setMapping(three, 3);
//and so on...
connect(one, SIGNAL(clicked()), &sm, SLOT(map()));
connect(two, SIGNAL(clicked()), &sm, SLOT(map()));
connect(three, SIGNAL(clicked()), &sm, SLOT(map()));
//and so on...
connect(&sm, SIGNAL(mapped(int)), this, SLOT(yourslothere(int)));
Note: QSignalMapper is VERY useful, keep that in mind ;)
I think you can try allocating the QPushButton in a array, something like this
QPushButton* numbers = new QPushButton[10];
And then, perform the connections using a for loop
for(size_t i = 0; i < 9; ++i)
{
connect(numbers[i],SIGNAL(clicked()),this,SLOT(writeNum()));
}
But I don't think it's worth. Explicit connection, while making the code more verbose, make the connections more clear to the reader.