PDA

View Full Version : Connecting signal from QDialog to slot from MainWindow classes



editheraven
4th April 2017, 20:15
I want that when the QDialog is closed by a button that emits "accepted();" signal, a slot in MaiWindow to capture the accept signal from QDialog. The slot is a custom method, show_table();.


//mainwindow

AddRecipe * addrecipes = new AddRecipe;
connect(ui->pushButton_4,SIGNAL(released()),this,SLOT(show_tab le()));
connect(addrecipes,SIGNAL(accepted()),this,SLOT(sh ow_table()));
....

//QDialog
connect(ui->pushButton_4,SIGNAL(clicked(bool)),this,SLOT(accep t()));
.....
void AddRecipe::on_pushButton_4_clicked()

// destroy();
emit accepted();
accept();

}

But show_table is never called. I've managed to use signal/slot mechanism in the same window, but not from another one.

d_stranz
4th April 2017, 22:49
It is not necessary for you to emit the QDialog::accepted() signal. This signal is emitted by the QDialog base class when you call the QDialog::accept() method.

You show three different connect() calls. The first one (line 4) won't compile because "ui->pushButton_4" is a private member of the AddRecipe class (unless your MainWindow class also happens to have a ui->pushButton_4 member, in which case you are connecting the wrong button).

The second (line 5) looks correct, as does the one in line 9. If you added a signal-slot connection in Qt Designer when designing your dialog, then you probably have two slots being executed when the button is clicked: the one you manually connect to in line 9, and the one that is generated for you by Qt Designer that connects the button's clicked() signal to on_pushButton4_clicked().

What you don't show us is the code where you actually show the AddRecipe dialog. My hunch is that your problem lies there.

Edit: Ah, I noticed that you attached the zip file with your code. My hunch is correct.



void MainWindow::on_actionAdd_New_triggered()
{

AddRecipe mDialogAddRecipe;
mDialogAddRecipe.setModal(true);
mDialogAddRecipe.exec();


}

You do understand that the AddRecipe instance you create -on the stack- here is not the same instance that you created in the MainWindow constructor, right? This instance has no connections to anything. And you also understand that the instance you create -on the heap- in the MainWindow constructor becomes basically an inaccessible dangling pointer once the constructor exits because you haven't saved the pointer anywhere, right?

The easiest solution is to get rid of the code in the constructor that deals with this dialog and move it into the slot:



void MainWindow::on_actionAdd_New_triggered()
{
AddRecipe addRecipes ( this );
connect( &addrecipes,SIGNAL(accepted()),this,SLOT(show_table ()));
addRecipes.exec();
}

Calling setModal() is redundant. By calling exec(), you -are- executing it as a modal dialog. Delete these lines from the MainWindow constructor:



AddRecipe * addrecipes = new AddRecipe;
connect(ui->pushButton_4,SIGNAL(released()),this,SLOT(show_tab le()));
connect(addrecipes,SIGNAL(accepted()),this,SLOT(sh ow_table()));


I haven't looked at the rest of your code, but unless your AddRecipe dialog somehow magically knows about your recipe database, anything that happens in the dialog disappears as soon as it closes because there aren't any other signals or slots connected to MainWindow or any data structures being retrieved from the dialog that tell MainWindow what has happened there. MainWindow's show_table() -can- access the dialog, but only temporarily, using the sender() method inside the show_table() slot. But once that slot exits, the dialog is gone.

editheraven
4th April 2017, 23:28
The pushbutton_4 in the mainwindow ui was just for testing to see if it can connect to the show_table method.
I haven't added any signal-slot connections into the UI.
You are so right, the instance created on the heap was useless as it couldn't be accessed when the constructor exited.
Now, with the connect in the dialog constructor, the method is accessed.

Anyway, the table model is not updated, but this is an issue I'll have to address later.

Thanks alot for your insights on this one.

d_stranz
5th April 2017, 03:40
Anyway, the table model is not updated, but this is an issue I'll have to address later.

I use the following pattern whenever I implement a dialog that collects non-trivial information from the user which then must be passed back to the main program:

- create a struct or class that holds the data to be filled in by the dialog. Both the dialog and the main window know about this struct
- before posting the dialog, fill an instance of the struct with appropriate default values
- store the struct instance in the dialog instance
- exec() the dialog
- if exec() returns QDialog::Rejected, do nothing
- if exec() returns QDialog::Accepted then do the following
- retrieve the modified copy of the struct from the dialog
- process it as appropriate to store it in the main app

The code would roughly go like this:



void MainWindow::on_actionAdd_New_triggered()
{
AddRecipe addRecipes ( this );
RecipeTemplate newRecipe; // the struct, filled by default values on construction
addRecipes.setRecipeTemplate( newRecipe );
if ( QDialog::Accepted == addRecipes.exec() )
{
newRecipe = addRecipes.getNewRecipe();
addNewRecipeToDB( newRecipe );
}
}

The dialog doesn't need any extra signals or slots, nor do I need to connect to any existing signals. Everything is handled internally to the MainWindow slot.

editheraven
5th April 2017, 13:29
I'll try this when I'll come from work.
As I can see, this works almost like a callback function system.

le : well I actually had too add

emit accepted();
to be able to update my table.

d_stranz
5th April 2017, 19:33
As I can see, this works almost like a callback function system.

Signals and slots are similar to that, yes. But in order for your dialog to do anything useful, it has to be able to communicate that to the rest of the program. So the callback (MainWindow slot in this case) has to receive data from the dialog. When calling this slot (by way of a signal), the dialog has to pass that information along.

So an alternative to my pattern above would be similar to what is used in QFileDialog: whenever the user changes to a new directory, selects a different file extension from the combobox, or selects a file, QFileDialog emits a signal that notifies of the change. For your AddRecipe dialog to work the same way, it would have to emit a signal that contains the data the user has entered for the new recipe, and MainWindow needs a slot to receive that data. A signal / slot pair that don't transmit any information (like your show_table() slot) means that you have to find some alternative means to get the information from the dialog.

I use the QFileDialog pattern when I need to keep track of changes occurring in the dialog while the dialog is still posted (like an "Apply" button that updates the main UI based on the current settings being defined in the dialog). If I don't need that, I generally use the pattern I described in the previous post.