PDA

View Full Version : Using 2 ui files in a project



ben1996123
3rd January 2013, 05:31
I have a QDialog with a pushbutton on it, and when I click the pushbutton, I want another QDialog window in a separate ui file to open. I have a dialog class (with the pushbutton in), but I can't figure out how to use the other ui file to create the window. I've included settings.h (the file that's automatically generated when I compile the program) and added it to the Ui namespace, then tried to create a settings object with "Ui::settings foo;", but I keep getting the same compiler error:

"c:\programs\qt\sim\dialog.h:67: error: C2079: 'Dialog::foo' uses undefined class 'Ui::settings'"

Santosh Reddy
3rd January 2013, 06:53
You may not have included the required ui file. See if this sample helps



#include "ui_OtherDialog.h"

void Dialog::on_pushButton_Clicked()
{
QDialog * other_parent = new QDialog(this);
Ui::OtherDialog other_ui;

other_ui.setupUi(other_parent);

other_parent.exec();
}

ben1996123
3rd January 2013, 15:29
You may not have included the required ui file. See if this sample helps



#include "ui_OtherDialog.h"

void Dialog::on_pushButton_Clicked()
{
QDialog * other_parent = new QDialog(this);
Ui::OtherDialog other_ui;

other_ui.setupUi(other_parent);

other_parent.exec();
}


Hmm... Just tried that, but now I'm getting an error whenever I access a member of ui (in the dialog class):

dialog.cpp:


#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent):
QDialog(parent),
ui(new Ui::Dialog),
{
srand(time(0));
ui->setupUi(this);
ui->label->setVisible(false);
}

void Dialog::on_pushButton_clicked(){ //settings
QDialog *parent = new QDialog(this);
Ui::settings foo;

foo.setupUi(parent);
parent->exec();
}


dialog.h:


#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QtCore>
#include <QtGui>
#include "settings.h"
#include "ui_settings.h"

namespace Ui {
class Dialog;
class settings;
}

class Dialog : public QDialog
{
Q_OBJECT

public:
Ui::Dialog *ui;
explicit Dialog(QWidget *parent = 0);
~Dialog();

private slots:
void on_pushButton_clicked();
};

#endif // DIALOG_H

Now, I get errors whenever I have "ui->" in my code.

Santosh Reddy
3rd January 2013, 15:44
Can you post the error?

ben1996123
3rd January 2013, 16:50
Can you post the error?

C:\programs\Qt\sim-build-desktop-Qt_4_8_1_for_Desktop_-_MSVC2010__Qt_SDK__Release\..\sim\dialog.cpp:21: error: C2027: use of undefined type 'Ui::Dialog'
C:\programs\Qt\sim-build-desktop-Qt_4_8_1_for_Desktop_-_MSVC2010__Qt_SDK__Release\..\sim\dialog.cpp:21: error: C2227: left of '->setupUi' must point to class/struct/union/generic type

d_stranz
3rd January 2013, 18:35
@ben: I think you are totally confused about how to go about what you want to do. You should not need to access the ui pointer in your other dialog at all. Ui pointers are meant to be hidden inside the widget classes that define them, so that they can access the signals, slots, and properties of the widgets defined in the ui file.

To invoke a second dialog as a response to a button click in the first dialog, all you need to do is something like this:



// Dialog1.cpp:
#include "Dialog1.h"
#include "Dialog2.h"

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

connect( ui->someButton, SIGNAL( clicked() ), this, SLOT( showDialog2() ) );
}

void Dialog1::showDialog2()
{
Dialog2 dlg2( this );
dlg2->exec();
}


where of course, you have defined the showDialog2 slot correctly in the Dialog1.h header. The "ui" pointers in the Dialog1 and Dialog2 classes should be declared as private, since there is absolutely no reason to access them from outside of the class the uses them.

If there are any signals or slots in Dialog2 that need to be "watched" from inside of Dialog1, then set up the connections in showDialog2(). These will automatically be disconnected when dlg2 goes out of scope at the end of showDialog2().

ben1996123
3rd January 2013, 18:57
@ben: I think you are totally confused about how to go about what you want to do. You should not need to access the ui pointer in your other dialog at all. Ui pointers are meant to be hidden inside the widget classes that define them, so that they can access the signals, slots, and properties of the widgets defined in the ui file.

To invoke a second dialog as a response to a button click in the first dialog, all you need to do is something like this:



// Dialog1.cpp:
#include "Dialog1.h"
#include "Dialog2.h"

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

connect( ui->someButton, SIGNAL( clicked() ), this, SLOT( showDialog2() ) );
}

void Dialog1::showDialog2()
{
Dialog2 dlg2( this );
dlg2->exec();
}


where of course, you have defined the showDialog2 slot correctly in the Dialog1.h header. The "ui" pointers in the Dialog1 and Dialog2 classes should be declared as private, since there is absolutely no reason to access them from outside of the class the uses them.

If there are any signals or slots in Dialog2 that need to be "watched" from inside of Dialog1, then set up the connections in showDialog2(). These will automatically be disconnected when dlg2 goes out of scope at the end of showDialog2().

That's similar to what I originally tried, but I was getting linker errors...

dialog.obj:-1: error: LNK2019: unresolved external symbol "public: virtual __thiscall settings::~settings(void)" (??1settings@@UAE@XZ) referenced in function "public: void __thiscall Dialog::showSettings(void)" (?showSettings@Dialog@@QAEXXZ)

dialog.obj:-1: error: LNK2019: unresolved external symbol "public: __thiscall settings::settings(class QWidget *)" (??0settings@@QAE@PAVQWidget@@@Z) referenced in function "public: void __thiscall Dialog::showSettings(void)" (?showSettings@Dialog@@QAEXXZ)

release\sim.exe:-1: error: LNK1120: 2 unresolved externals

d_stranz
3rd January 2013, 19:15
That's because you are trying to access some "Ui::settings" object. I have no clue what that is, and it is probably a bad design to try to access it in the way that you are doing, whatever it is.

What is it that you really want to do?

ben1996123
3rd January 2013, 23:29
That's because you are trying to access some "Ui::settings" object. I have no clue what that is, and it is probably a bad design to try to access it in the way that you are doing, whatever it is.

What is it that you really want to do?

Click a pushbutton and have it create a window from the ui file.

I basically copied the code that you posted, and I got the errors I posted before:


#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QtCore>
#include <QtGui>
#include "settings.h"

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
Q_OBJECT

public:
Ui::Dialog *ui;
explicit Dialog(QWidget *parent = 0);
~Dialog();

private slots:
void on_pushButton_clicked();
};

#endif // DIALOG_H


#include "dialog.h"
#include "ui_dialog.h"
#include "settings.h"

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

void Dialog::on_pushButton_clicked(){ //settings
settings a(this);
a.exec();
}

d_stranz
4th January 2013, 17:05
create a window from the ui file

I don't know what that means. The moc compiler converts the XML definition in the .ui file into C++ code, which is then compiled and linked into your executable. At run-time the Qt meta-object system executes that compiled code and instantiates QWidgets and layouts, sets their properties, and establishes connections between signals and slots. That's what the "ui->setupUi( this )" line does behind the scenes.

As far as I know, the only way to replicate this functionality at run-time (e.g. go from uncompiled XML code in a .ui file to running QWidget instances) is to use the QUiLoader or QFormBuilder classes.



void Dialog::on_pushButton_clicked()
{ //settings
settings a(this);
a.exec();
}


What is this "settings" class you are trying to create and exec()? Is that the name of your second dialog class? If it is, there is probably something wrong with the way it has been declared or implemented, but since you don't show the code for "settings.h" or "settings.cpp", there's no way to tell.

What happens if you use QFileDialog instead of "settings"?



#include <QFileDialog>

void Dialog::on_pushButton_clicked()
{ //settings
QFileDialog a(this);
a.exec();
}

ben1996123
4th January 2013, 17:16
void Dialog::on_pushButton_clicked()
{ //settings
settings a(this);
a.exec();
}


What is this "settings" class you are trying to create and exec()? Is that the name of your second dialog class? If it is, there is probably something wrong with the way it has been declared or implemented, but since you don't show the code for "settings.h" or "settings.cpp", there's no way to tell.

What happens if you use QFileDialog instead of "settings"?



#include <QFileDialog>

void Dialog::on_pushButton_clicked()
{ //settings
QFileDialog a(this);
a.exec();
}


The settings class is the second dialog class. I added the settings.h and settings.cpp files and straight away it wouldn't compile, even with the default code. Using QFileDialog compiles and works fine.

settings.h:

#ifndef SETTINGS_H
#define SETTINGS_H

#include <QDialog>

namespace Ui {
class settings;
}

class settings : public QDialog
{
Q_OBJECT

public:
explicit settings(QWidget *parent = 0);
~settings();
Ui::settings *ui;

private:
};

#endif // SETTINGS_H

settings.cpp:

#include "settings.h"
#include "ui_settings.h"

settings::settings(QWidget *parent) :
QDialog(parent),
ui(new Ui::settings) //error: C2512: 'Ui::settings' : no appropriate default constructor available
{
}

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

d_stranz
4th January 2013, 17:36
ui(new Ui::settings) //error: C2512: 'Ui::settings' : no appropriate default constructor available

There is probably something wrong with the code generated by the moc compiler to build the ui .h and .cpp files. Have you done a clean rebuild of everything in your project? Is your ui file correct?

The compiler error says that you have declared some C++ class called "settings" that lives in the "Ui" namespace, but you haven't provided any code that tells the compiler how to create one (using new). This implies that the name in your ui file is not "settings" but is something else. Have you opened "ui_settings.h" in your editor and looked at the code moc is generating for you?

In my personal coding style, I do not consider it to be a good practice to use the same name for both the Ui class and the QWidget classes (you have used "settings" for both). I always choose different (but related names) to avoid confusion about which class is being used in a given context. So, in my case, I would use the name "SettingsDlg" or "SettingsDialog" as the name of the derived QDialog class. (The C++ compiler can keep it all straight - it is the humans who get confused easily).

Also, there is no reason why the "ui" pointer member variable should be public; it should almost always be a private (or at best, protected) member. If you need to access QWidget instances that are declared in the ui code, then it is always better to create access functions for that purpose:



QPushButton * SettingsDialog::getPushbutton() const
{
return ui->pushbutton;
}


If you write any code that is intended for use by other people, directly exposing the ui pointer as you have done is an invitation to abuse. Suppose someone decided that they would replace the ui->pushbutton pointer with a pointer to a QTableWidget instead? Exposing ui as a public member variable allows that to happen. Creating an access function (getPushbutton()) lets them retrieve the pushbutton instance (and mess with its properties), but they can't change the pointer itself. (They could delete it, but that's another issue).

ben1996123
4th January 2013, 18:09
Also, there is no reason why the "ui" pointer member variable should be public; it should almost always be a private (or at best, protected) member.

Yeah I know, not sure why I changed it, I guess I was becoming desperate and was just trying random stuff hoping it would work :p

Anyway, I fixed it now. I guess there must have been something wrong with the ui file; I deleted it created a new one and it works fine now.

d_stranz
4th January 2013, 18:18
I guess I was becoming desperate and was just trying random stuff hoping it would work

That's never a good tactic. How do you decide which random thing (or combination of them) was the one that actually made it work? The compiler is usually pretty smart, it is just hard to figure out sometimes what it is trying to tell you, especially when you are in the mind-set that says, "There's nothing wrong with this code."

ben1996123
4th January 2013, 21:12
That's never a good tactic. How do you decide which random thing (or combination of them) was the one that actually made it work? The compiler is usually pretty smart, it is just hard to figure out sometimes what it is trying to tell you, especially when you are in the mind-set that says, "There's nothing wrong with this code."

Yeah I know... I usually don't do it...


If there are any signals or slots in Dialog2 that need to be "watched" from inside of Dialog1, then set up the connections in showDialog2(). These will automatically be disconnected when dlg2 goes out of scope at the end of showDialog2().

For this, I've connected the signals and slots, but when I open the settings window and click on pushButton_3, it runs the code correctly but it also runs the code that is supposed to run when I click on pushButton_3 in the dialog window. Do I have to rename all of the widgets in the second window to avoid this or am I just doing something wrong?

d_stranz
4th January 2013, 23:04
am I just doing something wrong?

You're doing something wrong. ;)

pushButton_3 is a member of the "settings" dialog ui? Do you have a pushButton_3 that is also a member of the first "dialog" class ui? These are independent.

It looks like you are using Qt Designer to set up the connections between signals and slots. Are you sure you are connecting together the right button instances in the designer? Sounds to me as though you have connected the clicked() signal from the pushButton_3 in "settings" to two different slots, one in "settings" and one in "Dialog1". In this case, clicking the pushButton_3 in settings will cause two slots to be executed.

I usually like to set up my connections in code; personal preference, but it lets me see in one place what signals and slots are connected, rather than having to look in both a C++ file and a UI file to be sure of what is going on.

ben1996123
5th January 2013, 00:02
pushButton_3 is a member of the "settings" dialog ui? Do you have a pushButton_3 that is also a member of the first "dialog" class ui? These are independent.

Yes, there are 2 buttons called pushButton_3, in separate classes.


It looks like you are using Qt Designer to set up the connections between signals and slots. Are you sure you are connecting together the right button instances in the designer?

To set up the signals and slots, I right clicked on pushButton_3 in settings.ui and clicked on Go to slot.


Sounds to me as though you have connected the clicked() signal from the pushButton_3 in "settings" to two different slots, one in "settings" and one in "Dialog1". In this case, clicking the pushButton_3 in settings will cause two slots to be executed.

I usually like to set up my connections in code; personal preference, but it lets me see in one place what signals and slots are connected, rather than having to look in both a C++ file and a UI file to be sure of what is going on.

Hmm... Not sure how I could have done that...

Is there any way to see/modify all the signals and slots that have been added in Qt Designer?

d_stranz
6th January 2013, 20:34
Is there any way to see/modify all the signals and slots that have been added in Qt Designer?

Probably, but it is easier just to open the .ui file in a text editor and examine the connections part of the XML (usually near the end of the file). Or run the program in debug mode and print out the identity of the sender in each of the slots. If the sender is the same in both slots when you click on the pushbutton in the settings dialog, then somehow you managed to connect the wrong button to one of them.

It is a really, really good practice to name your UI controls something sensible and meaningful, rather than meaningless names like "pushbutton_3" at Qt Designer does by default. You can easily rename the widgets in the designer - just click on the name in the properties and change it. When you end up with multiple dialogs, and all of the pushbuttons are named "1", "2", "3", ... it is easy to get yourself confused, and impossible to remember when looking at the code what "pushbutton_1" does in this dialog vs. what the button with the same name does in a completely different dialog.

The problems you are having are the main reason I said I prefer to set up my connections in code, rather than use the designer to do it. It may be easier to use the designer, but after you are done, there is no way to look at your C++ code and tell at a glance what's going on. You have to look in several places to figure it out, and changing it leads to more places to mess up. When you do it in code in the dialog constructor, it's dead simple and obvious what is going on:



connect( ui->showSettingsBtn, SIGNAL( clicked() ), this, SLOT( onShowSettings() ) );


The button is named something sensible, easy to remember, and obvious what happens when it is clicked, and the slot is named similarly.

ben1996123
6th January 2013, 23:31
Probably, but it is easier just to open the .ui file in a text editor and examine the connections part of the XML (usually near the end of the file). Or run the program in debug mode and print out the identity of the sender in each of the slots. If the sender is the same in both slots when you click on the pushbutton in the settings dialog, then somehow you managed to connect the wrong button to one of them.

It is a really, really good practice to name your UI controls something sensible and meaningful, rather than meaningless names like "pushbutton_3" at Qt Designer does by default. You can easily rename the widgets in the designer - just click on the name in the properties and change it. When you end up with multiple dialogs, and all of the pushbuttons are named "1", "2", "3", ... it is easy to get yourself confused, and impossible to remember when looking at the code what "pushbutton_1" does in this dialog vs. what the button with the same name does in a completely different dialog.

The problems you are having are the main reason I said I prefer to set up my connections in code, rather than use the designer to do it. It may be easier to use the designer, but after you are done, there is no way to look at your C++ code and tell at a glance what's going on. You have to look in several places to figure it out, and changing it leads to more places to mess up. When you do it in code in the dialog constructor, it's dead simple and obvious what is going on:



connect( ui->showSettingsBtn, SIGNAL( clicked() ), this, SLOT( onShowSettings() ) );


The button is named something sensible, easy to remember, and obvious what happens when it is clicked, and the slot is named similarly.

Thanks for the advice; I remade both ui files and named all the widgets and it works fine now.

d_stranz
7th January 2013, 05:13
Thanks for the advice

Sorry if I sound "preachy"; I went through this same Qt learning curve 5 or so years ago and had all the same problems. I still run into head-scratching situations. There's a lot to learn before you reach the level of competence of ChrisW, Wysota, Santosh, and some of the others on this forum.