PDA

View Full Version : Help with signal and slot in a UI component



mossen
12th May 2011, 20:33
I have a ButtonBox with an OK and a Cancel button, and I want to hook up the signals so that when the button is pressed, a method in my form is called. The form is called EditDialog. But I'm having trouble doing it.

In QtDesigner, in the "Signals & Slots Editor", I added an entry:
Sender: buttonBox
Signal: clicked(QAbstractButton *)
Receiver: Dialog
Slot: accept()

The thing is, in the Receiver drop-down, there is no option for "EditDialog", just Dialog and a few other UI components. So its not working. I can do the signal/slot mechanic when I emit a signal manually, but for a button click, what do I need to do to get the signal to emit?

ChrisW67
12th May 2011, 23:49
"Dialog" is the default name of the QDialog object created when the Designer wizard creates a dialog based on "Dialog with buttons ..." template. Have you changed that name in the Object Inspector panel (at the top of the hierarchy) to EditDialog?

Alternatively, have you written your own QDialog derivative class called EditDialog that you want Designer to use in place of QDialog? To do this you use the promotion features in Designer.

Connecting the clicked(QAbstractButton *) signal to a parameterless accept() slot is probably not useful: clicking any button in the box will trigger accept() (if the connection is made at all). The template in Designer already connects the OK/Cancel buttons to the accept() and reject() slots of the QDialog anyway.

mossen
13th May 2011, 00:21
Hmmm, OK. Thank you for that. I managed to change "Dialog" to "EditDialog". Then I reran qmake, which I think broke things. ui_editdialog.h was overwritten, and all the code I wrote in that file is gone. I am getting the feeling I'm not supposed to be writing code in there....where should it go? Am I meant to have a separate editdialog.h?

// ui_editdialog.h - question in the comments


....
#include <QtGui/QLineEdit>
#include <QtGui/QPushButton>

QT_BEGIN_NAMESPACE

class Ui_Dialog // why isn't it called Ui_EditDialog? "Dialog" is no longer in the project
// also, I thought it was supposed to inherit from QDialog
{
public:
QGridLayout *gridLayout;
QLabel *label;
....


Also, why is this at the bottom:


namespace Ui {
class Dialog: public Ui_Dialog {};
} // namespace Ui


Am I supposed to inherit EditDialog from Ui_Dialog? Or from Dialog? Why is Ui_Dialog not a descendant of QDialog? Designer seems to imply that it is a QDialog, but qmake doesn't generate the code for it...

ChrisW67
13th May 2011, 01:10
The ui_editdialog.h file is generated from the .ui file Designer creates and carries this banner at the top:


/************************************************** ******************************
** Form generated from reading UI file 'untitled.ui'
**
** Created: Fri May 13 08:47:32 2011
** by: Qt User Interface Compiler version 4.7.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
************************************************** ******************************/
So, yes... you shouldn't be editing this file.

There are three files to the typical dialog class with Designer interface. The UI file, and a class header and implementation. You write the last two (or Qt Creator creates a skeleton for you) and they use the ui_dialog.h file generated from the UI. This is what the skeleton typically looks like:


// Header
#ifndef EDITDIALOG_H
#define EDITDIALOG_H

#include <QDialog>

namespace Ui {
class EditDialog;
}

class EditDialog : public QDialog
{
Q_OBJECT

public:
explicit EditDialog(QWidget *parent = 0);
~EditDialog();

protected:
void changeEvent(QEvent *e);

private:
Ui::EditDialog *ui;
};

#endif // EDITDIALOG_H



// Implementation
#include "editdialog.h"
#include "ui_editdialog.h"

EditDialog::EditDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::EditDialog)
{
ui->setupUi(this);
}

EditDialog::~EditDialog()
{
delete ui;
}

void EditDialog::changeEvent(QEvent *e)
{
QDialog::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
(This aggregates the UI class using a pointer but there are variations on this theme). These are the files you add slots to and use to create a new EditDialog to display to the unsuspecting world.

You can:

Rely on QMetaObject::connectSlotsByName() (called in ui_dialog.h) to connect the actual widgets to slots by naming the slots appropriately.
Make explicit connections between the UI and the containing class in the constructor after the setupUi() call (my preference).
Use the Slots editor in Designer to connect the signals to slots but you need to manually add the slots to the available list.

mossen
13th May 2011, 01:59
Thank you, ChrisW67. It is beginning to make more sense!

Added after 43 minutes:

Wait, I spoke too soon, I am back to not understanding. I seem to have 4 files associated with the class:

the Designer-generated UI file - editdialog.ui
uic-generated header - ui_editdialog.h
class header file - editdialog.h
implementation - editdialog.cpp

I don't understand why uic is putting a class declaration for EditDialog in ui_editdialog.h. Now I can't declare the class in my own header file without deriving a new class from it. Is this how its supposed to work? It just seems weird to have 3 layers of inheritance for what is essentially one class.

ChrisW67
13th May 2011, 07:13
There are four files, but only three are source files you need to concern yourself with and one is an intermediate product generated by uic (qmake looks after this).

uic will be generating a class called EditDialog in the Ui namespace. Your class EditDialog will be in the default namespace (unless you do something to change that). In my header example at line 8 is a forward declaration of the EditDialog in Ui (ultimately implemented by generated code). This is followed by EditDialog in the default namespace. The names don't clash because of the namespaces, but if it confuses you then by all means give the Ui class another name and change line 8 of the header and 7 of the implementation to match.

DanH
15th May 2011, 03:44
Many times it's simpler to manually code the connect() statements you need rather than using the Designer. Among other things there are bugs in a couple of versions of Designer that make it go "poof" randomly when editing signals, and you also run into odd situations where you can't easily connect the pieces you want. With coded connect() statements you know exactly what you're getting.

(Hint, always code some sort of test of the connect() return code. Eg:

bool success = connect(...);
Q_UNUSED(success);
Q_ASSERT(success);
That way you're assured that there aren't any syntax errors.)