QT Signals & slots in diffrent class - c++

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

Related

Qt connect in constructor not connecting?

A google search gives these as the top three results:
Qt: Connect inside constructor - Will slot be invoked before object is initialized?
Qt can I connect signals/slots to self in constructor?
QT Connect Signal Slot and initialize in constructor
According to those, it seems like it ought to "just work" like anything else. But this code doesn't:
EditorList::EditorList(..., QWidget* parent) :
QWidget(parent)
{
...
Processing* processing = Processing::getInstance();
connect(this, SIGNAL(reorderDelete(DataSequence*,ListType,QList<int>)), processing, SLOT(reorderDelete(DataSequence*,ListType,QList<int>)));
...
buttonDelete = new QPushButton(this);
connect(buttonDelete, SIGNAL(clicked(bool)), this, SLOT(buttonDeleteClick()));
...
}
...
void EditorList::buttonDeleteClick()
{
...
QList<int> locations;
...
emit reorderDelete(mySequence, myListType, locations); //breakpoint 1 here
}
//-----------------------------------------------------------------
void Processing::reorderDelete(DataSequence* sequence, ListType listType, QList<int> locations)
{
if(sequence) //breakpoint 2 here
{
sequence->reorderDelete(listType, locations);
}
}
The reason for this structure, instead of calling mySequence->reorderDelete directly, is to have it done in Processing's thread instead of the UI's. I hope I haven't stripped out too much detail to show the problem; this is a rather large project.
When I click my delete button, I hit breakpoint 1 (so far, so good), but I don't hit breakpoint 2. My other signals/slots work across threads, but their connects are not in constructors. I want to make this one automatic so that every instance is "just connected" without having to remember to do it. Can I not do that?
Okay, I got it. Leaving up for others to find.
According to this, my ListType enum was blocking the system from making the connection. It only works with system-known datatypes because emitting a SIGNAL actually stores a copy for the SLOT(s) to read later. I knew that, but I thought it was more like a stack frame that could take anything. Apparently not.
It also works to put a call to qRegisterMetaType<ListType>("ListType"); somewhere before the connect. (I put it in my main window's constructor.) This makes the datatype known so that the connection can work anyway.
I'm hitting both breakpoints now.
Make sure you have used Q_OBJECT macros in your class

Qt custom QPushButton clicked signal

I want to send two integers, string and fret, to a SLOT that will process the location of the button that was pressed. The SIGNAL and SLOT argument have to match so I am thinking I need to reimplement the QPushButton::clicked event method. Problem is I am new to Qt and could use some direction.
connect(&fretBoardButton[string][fret], SIGNAL(clicked()), this, SLOT (testSlot()));
If you use the C++11 connection syntax you can use a lambda with calls testSlot with your string and fret arguments:
connect(&fretBoard[string][fret], &QPushButton::clicked, [this, string, fret]() {
testSlot(string, fret);
});
This code creates a lambda using the [captures, ...](arguments, ...) { code } syntax. When you make the connection it captures the string and fret variable values, and will then pass them on to testSlot when the button is clicked.
There are Two approaches you could use to add the string and fret information. one is to use the sender() function to get the button which emitted the signal. you can the access fret and string if they are members of your button class so in the SLOT you would have.
MyPushButton *button = (MyPushButton *)sender();
button.getFret();
button.getString();
However since you are already subClassing QPushButton you could use a private SLOT to catch the buttonClicked signal and re-emit a signal with the right values.
In the constructor
connect(this, SIGNAL(clicked()), this, SLOT(reemitClicked()));
and then the reemit SLOT
void MyPushButton::reemitClicked()
{
emit clicked(m_fret, m_string);
}
be sure to add the appropriate private slot and public signal to you class
https://doc.qt.io/archives/qq/qq10-signalmapper.html see this artical for a good discussion on various ways to add an argument to signal.

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

How to tell when a QPushButton is clicked in a QButtonGroup

In my project, I have 40 QPushButtons all put into a QButtonGroup like this:
QButtonGroup* group = new QButtonGroup(this);
group->addButton(ui->slot_0);
group->addButton(ui->slot_1);
//...
group->addButton(ui->slot_38);
group->addButton(ui->slot_39);
Each button is a QPushButton that I made checkable. That way only one button can be checked at a time. All works great, but how can I "make a slot" when one of the buttons becomes checked? I don't want to have 40 different slots, one for each button all to end up doing essentially the same thing. Is there any way I can just use the QButtonGroup I put them in?
As Jamin and Nikos stated: you should create your own slot to handle the signal emitted by QButtonGroup. It could be something like this:
In the header file:
public slots:
void buttonWasClicked(int);
In the *.cpp file:
void MainWindow::buttonWasClicked(int buttonID)
{
cout << "You have clicked button: " << buttonID << endl;
}
And in the code responsible for creation of the MainWindow (i.e. in constructor but not necessairly) there should be this line:
connect(group, SIGNAL(buttonClicked(int)), this, SLOT(buttonWasClicked(int)));
Be aware that since Qt5 the connect syntax has changed. The syntax I used here is from Qt4. It still works but is deprecated now (for more information please refer to New Signal Slot Syntax in Qt 5). Moreover I would suggest going through QButtonGroup class reference as there are other available signals which could suit your needs better than the one I've chosen.
BR
The documentation for QButtonGroup shows a QButtonGroup::buttonClicked() signal - have you already tried that one?
The signal comes in two variants - one that gives the QPushButton as a parameter (as a QAbstractButton), and one that gives the ID of the button in the group.
You can use connect() to setup signal and slot connections in your C++ code.
Sometime during the initialization of your window's class (perhaps in the constructor), call this:
connect(myButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(theSlotThatYouWrite(QAbstractButton*));
Where myButtonGroup is probably this->ui->nameOfTheButtonGroup, and theSlotThatYouWrite is a function that you write in your own code, that belongs to your window's class, that returns void and takes a signal QAbstractButton* as a parameter (since that's what this specific signal gives as an argument).
Make sure theSlotThatYouWrite is under the label "private slots:" or "public slots:" in your class's interface.
Here's a screenshot of actual usage of some signals and slots in my own code.
Signals and Slots is something very important to learn, but can be bit of a hill to climb when first trying to understand it!

Qt: connecting signal to slot having more arguments

I want to connect a signal clicked() from the button to a slot of different object.
Currently I connect signal to helper method and call desired slot from there:
connect(button, SIGNAL(clicked()), this, SLOT(buttonClicked()));
void buttonClicked() { // Helper method. I'd like to avoid it.
someObject.desiredSlot(localFunc1(), localFunc2());
}
But maybe there is a more simple and obvious way to do this?
is this what you want to do:
the signal clicked should be connected to the "desiredSlot" which takes two arguments that are returned by localFunc1 & 2 ??
this is not possible, as you can read in the QT docs. A slot can take less arguments than provided by the signal - but not the opposite way! (The documentation says "This connection will report a runtime error")
This ought to work with the new signal/slot mechanism in qt 5:
connect( button, &QPushButton::clicked, [&](){ someObject.desiredSlot( localFunc1(), localFunc2() ); } );
You will need to adjust the lambda capture to your needs.
In some cases, default arguments may help, e.g. declare desiredSlot as:
desiredSlot(int a=0, int b=0)
You cannot access members in default argument though.
That is not the way to connect signals and slots in QT. You should use:
connect(button, SIGNAL(clicked()), receiver, SLOT(slotToBeCalled());
Have a look at the QT documentation.