PDA

View Full Version : Click Widget button from MainWindow



rmkeller
3rd November 2019, 18:37
Hello Together,

for the last six hours I tried to click a button on a Widget with a function located in the MainWindow class.

I also read a lot in several Formus about this Signal Slot concept.
The best I found is
https://www.qtcentre.org/threads/49866-connect-mainwindow-with-self-made-widgets

But even with this I faild to understand the core problem.

So I if somebody could explain in more detail what to do, could help a lot of people.

I use the QT basic Example "CAN BUS Example"
With QT creator 4.10.1 I droped a new pushButton on the surface of the MainWindow.

Now I simply want to push the "sendbutton" located inside a widget "sendframebox"
Lets say I want to push the button with a timer like this

QTimer::singleShot(2000, []{ qDebug("Button "sendButton" located in sendframebox should be pressed");});

Or a Winwdow function when someting happend...


My code in MainWindow works like this:



void MainWindow::on_radioButton_RUNloop_clicked()
{

if(m_ui->Skript_checkbox_send->isChecked())
{
m_ui->sendFrameBox->sendButton_4(); <- HOW TO PUSH THE BUTTON of the widget??
}

QTimer::singleShot(2000, []{ qDebug("Or push the button with this singleshot timer");});
}

Can somebody tell me how to solve this with the signal slots?

Or how to handle this problem quick and dirty?



Added after 23 minutes:

For the other beginners I solved it quick and dirty with public pushButton function.
But my guess is that it is not really the best solution.

Here the way I solved it "dirty"

In the widget *.h locate a public function:


public:
....
void pushSEND4btn();
...

In the widget *.cpp locate the wrapper code.

void SendFrameBox::pushSEND4btn()
{
m_ui->sendButton_4->click();
}


Now you can press this button from MainWindow:

m_ui->sendFrameBox->pushSEND4btn();

I hope somebody can tell us the right way to solve such a situation.

d_stranz
4th November 2019, 19:18
QPushButton inherits from QAbstractButton. This base class has a slot QAbstractButton::click(). In your pushSEND4btn() method, you are calling this slot as an ordinary function.

This works, but as you say, it is the "dirty" way to do it, because it exposes internal details of the SendFrameBox class. What happens if you change your UI, and you no longer use a QPushButton or this "SendFrameBox" widget (whatever that is)? Your code would break, because the details of the implementation have changed, and your MainWindow was depending on them.

The "correct" (i.e. Qt) way is to use signals and slots to communicate between different parts of your program. In your case, one way to do this could be to:

1 - Implement a signal for your MainWindow class that get emitted when you want to send a message. Call it "sendMessage()" and declare it in your MainWindow.h file in the signals: section.
2 - Now, you can implement a slot in your MainWindow class that can be called any time you want this signal emitted. Call that slot "doSendMessage()" or something meaningful. So, for example, maybe you have a menu item (Edit -> Send Message), and you have defined the QAction for that menu item. Then, in the MainWindow constructor, you would write:



MainWindow::MainWindow( QWidget * parent )
: QMainWindow( parent )
{
// call setupUi(), etc.

QAction * pSendAction = new QAction( "Send Message", this );
connect( pSendAction, &QAction::triggered, this, &MainWindow::doSendMessage );

// set up the SendFrameBox

SendFrameBox * pFrameBox = new SendFrameBox( this );
connect( this, &MainWindow::sendMessage, pFrameBox, &SendFrameBox::handleSendMessage );

// do more MainWindow construction
}

void MainWindow::doSendMessage()
{
emit sendMessage();
}


3 - Now, in any class where you want something to happen when MainWindow emits the sendMessage() signal, you can connect a slot to it. This slot can do anything, not just simulate pushing a button. So, for example, in your SendFrameBox widget, you can declare and implement the "handleSendMessage()" slot:



void SendFrameBox::handleSendMessage()
{
// do anything you want, like clicking the button
}


4 - But it would be even better if you could remove the dependence on handling the sendMessage() signal from anything related to a pushbutton or where in the UI that button is found. After all, the pushbutton is simply the way you get your UI to do something on the CAN bus. Again, a Qt way of doing this would be to define a QAction that can be directly connected to whatever method you use to actually send something to the CAN bus.

Then, the MainWindow sendMessage() signal could be connected to this QAction's trigger() slot. If you have a QPushButton in the SendFrameWidget class, you can connect that button's clicked() signal to the same QAction's trigger() slot. This way, either the MainWindow's menu action -or- the pushbutton click cause exactly the same code to happen - QAction::trigger() slot is called.

Assuming you have a manager class that directly handles communication with the CAN bus, this class should have a slot that is connected to the QAction::triggered() signal.

The result is that you have disconnected the process of sending a message to the CAN bus from almost all details of the UI. The only dependence you have are through the QAction, and you can connect its trigger() slot to any UI component you want. You can change the UI to anything, and the only code you have to change is the connection between the UI parts that have changed and the QAction slot.