PDA

View Full Version : Qt Designer Partial Qt Designer built forms



pkohut
28th December 2010, 16:22
I've got a partial form layout built with Qt Designer, the rest of the form is dynamically built at run time during application initialization.

The auto generated code by Qt Creator adds a call to QMetaObject::connectSlotsByName at the end of the setupUi function. I'd like to defer this call until after the UI is built at run time. Is there a switch in Qt Designer or Creator to suppress generating that line of code?

If need be, a pre build step to strip the line of code from the generated header could be done before compiling, but I'd rather not go that route.

TIA,
Paul

high_flyer
28th December 2010, 16:30
The auto generated code by Qt Creator adds a call to QMetaObject::connectSlotsByName at the end of the setupUi function.
I can't test at the moment, but as far as I know, this is done by the moc, not by the uic.
The uic as far as I can remember, only generates a header, which initialized the gui elements.
The signal/slot connections are done by the moc.
But I might be wrong, as I said, I can't test here at the moment.
If I am right, your problem is not with the code generated by uic.

pkohut
28th December 2010, 18:52
Thanks high_flyer,

After running some tests and looking at the source for connectSlotsByName, I have a clearer picture of what is happening behind the scenes. My misunderstanding was that connectSlotsByName(this) would auto magically match up all the children and relative slots of this with other children and relative signals.

Now it's my understanding, when connectSlotsByName(this) is called that only slots of this will be matched up with the child objects with matching signals.

About ui_MainWindow.h and connectSlotsByName being called in setupUi - would be nice to disable that call from being generated, so it can be called later after the full UI is initialized.

Thanks again h_f.

high_flyer
28th December 2010, 19:18
setupUi() is not called in the auto generated code, but in your code... or I misunderstood you...

pkohut
28th December 2010, 19:46
setupUi() is not called in the auto generated code, but in your code... or I misunderstood you...

Correct, from the constructor of the derived class QMainWindow.


MainWindow::MainWindow(QWidget ...)
{
ui.setupUi(this);
// UI is only partially built from Qt Designer, connectSlotsByName
// is called in setupUi.

BuildRestOfUI(this);
// Can't call connectSlotByName a second time, otherwise each signal
// slot pairing from the previous call will add another signal slot pairing
// resulting in those slots getting 2 signals (called twice).
}




//** Form generated from reading UI file 'MainWindow.ui'
void setupUi(QMainWindow *MainWindowClass)
{
if (MainWindowClass->objectName().isEmpty())
MainWindowClass->setObjectName(QString::fromUtf8("MainWindowClass"));
MainWindowClass->resize(766, 542);
actionNew = new QAction(MainWindowClass);
actionNew->setObjectName(QString::fromUtf8("actionNew"));
// >>snip<<
retranslateUi(MainWindowClass);
QMetaObject::connectSlotsByName(MainWindowClass);
} // setupUi

high_flyer
28th December 2010, 20:03
Oh now I see what you mean (I think) - you DO want to call setupUi() when you do, but you don't want it to call the signal/slot auto connection at that time, is that correct?
Do you have to use auto-connect?
If you don't - you can delete the connections done by designer, this way setupUi() will not call the connections, and you can make the connections at any time later that suits you.

If you have to use the auto connections, then maybe you need a different approach.
Have a look at QAbstractFormBuilder, it might give you the flexibility you want, without giving up on the ui files.

wysota
28th December 2010, 20:53
I would suggest to not rely on connectSlotsByName() at all and instead make all the connections manually. auto-connecting slots is a dirty hack that shouldn't be used as it requires the slots to have weird names and the mechanism often backfires if you name slots in this manner by accident and you end up with a double connection or no connection at all (if you rename some object later on).

pkohut
28th December 2010, 20:59
Oh now I see what you mean (I think) - you DO want to call setupUi() when you do, but you don't want it to call the signal/slot auto connection at that time, is that correct?
Correct.

Do you have to use auto-connect?
If you don't - you can delete the connections done by designer, this way setupUi() will not call the connections, and you can make the connections at any time later that suits you.
Prior to the initial posting I did that, however the results were unexpected (to me). Here's the code. Running it with QMetaObject::connectSlotsByNames commented out in setupUi resulted in found connections (wasn't expecting any). Will do some research as to what connections those are and if safe to disconnect and will they reconnect with a later call to connectSlotsByNames. Post findings later.



MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
bool bFoundConnections = false;
foreach(QObject * obj, this->children()) {
bFoundConnections |= obj->disconnect();
}

if(bFoundConnections) {
qDebug() << "It appears QMetaObject::connectSlotsByName was called"
<< "in ui.setupUi, make sure that line of code is commented out.";
}
BuildResultOfUi(this);
}




If you have to use the auto connections, then maybe you need a different approach.
Have a look at QAbstractFormBuilder, it might give you the flexibility you want, without giving up on the ui files.
Don't need auto connections, just trying to come out of the stone age of hand coding every thing.

Thanks very much for your help.

Added after 5 minutes:


I would suggest to not rely on connectSlotsByName() at all and instead make all the connections manually. auto-connecting slots is a dirty hack that shouldn't be used as it requires the slots to have weird names and the mechanism often backfires if you name slots in this manner by accident and you end up with a double connection or no connection at all (if you rename some object later on).

That's a good point. There is one slot/signal pair so far that just won't auto connect (spent about 30 minutes on it so far), I'm sure it's syntax, however it looks correct.

high_flyer
28th December 2010, 21:03
Don't need auto connections, just trying to come out of the stone age of hand coding every thing.
Like wysota, I don't like auto connections, and never used them.
Nothing stone aged about connecting your self (IMHO) - its a matter of control over your code.

If you don't need auto connections, and even better, if you don't need the connections done at ui design time (in designer) just get rid of them (any connections in the ui), make them manual - it will save you all the this headache!

wysota
29th December 2010, 00:55
As a side note, there is a quick way to disconnect all connections made to an object so a potential solution to your problem is to do just that (by iterating over children and calling disconnect() variant that breaks all the connections between objects) and then to call connectSlotsByName() when you need it. But I really advise you to not use it and instead make all the connections conciously.

pkohut
29th December 2010, 07:52
As a side note, there is a quick way to disconnect all connections made to an object so a potential solution to your problem is to do just that (by iterating over children and calling disconnect() variant that breaks all the connections between objects) and then to call connectSlotsByName() when you need it. But I really advise you to not use it and instead make all the connections conciously.

I'm taking the advise of you and high_flyer and forgoing auto connections. However, was still wondering if breaking the connections then calling connectSlotsByName would be sufficient. From the code below I'd say the answer is no.


MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
// connectSlotsByName called in setupUi
ui.setupUi(this);

// Get count of disconnected slots and signals
int nConnectionsA = 0;
foreach(QObject * pObj, this->children())
nConnectionsA += pObj->disconnect();

// reconnect slots by name
QMetaObject::connectSlotsByName(this);

// Get count of disconnected slots and signals
int nConnectionsB = 0;
foreach(QObject * pObj, this->children())
nConnectionsB += pObj->disconnect();

// Test run here nConnectionsA == 9 and
// nConnectionsB == 0
Q_ASSERT(nConnectionsA == nConnectionsB);
}

high_flyer
29th December 2010, 08:55
Your code is not doing what you wanted to do:


int nConnectionsA = 0;
foreach(QObject * pObj, this->children()){
if( pObj->disconnect())
nConnectionsA ++;
}

// reconnect slots by name
QMetaObject::connectSlotsByName(this);

// Get count of disconnected slots and signals
int nConnectionsB = 0;
foreach(QObject * pObj, this->children()){
if( pObj->disconnect())
nConnectionsB ++;
}

pkohut
29th December 2010, 10:02
Your code is not doing what you wanted to do:
Hmm, what did I miss? QObject::disconnect() returns type bool. Through implicit conversion from type bool to type int , true is 1 and false is 0.

Just to make sure it isn't a compiler thing I tested this is VC and gcc.

bool bVal = (bool) 200;
int nVal = bVal;
qDebug() << bVal << nVal;
Output is: true 1

high_flyer
29th December 2010, 10:32
You are right.
But I think such tings are potentially dangerous.
Since true doesn't necessarily has to mean '1' (all though I just checked, and the c++ indeed specifies true as '1').
But some environments or applications redefine bool,
But in this case you are right.

what does this->children().size() return in both cases?

wysota
29th December 2010, 10:42
However, was still wondering if breaking the connections then calling connectSlotsByName would be sufficient.
But what is the point of doing so? Could you tell me what the following slot does?


void Cls::on_btn4_clicked() { /* ??? */ }

Isn't it better to call it say... "chooseBrushColor()" instead?

pkohut
29th December 2010, 12:43
You are right.
But I think such tings are potentially dangerous.
Since true doesn't necessarily has to mean '1' (all though I just checked, and the c++ indeed specifies true as '1').
But some environments or applications redefine bool,
But in this case you are right.

Integral promotion rules for type bool are explained in the C++ standard, sections 3.9.1.6, 4.5.4, and 4.12.

Added after 34 minutes:


But what is the point of doing so? Could you tell me what the following slot does?

void Cls::on_btn4_clicked() { /* ??? */ }
Isn't it better to call it say... "chooseBrushColor()" instead?

(edit: somehow lost my original response)
Yes, "chooseBrushColor" is much better than "on_btn4_clicked". However, renaming "btn4" to "btnBrushColor" makes a much more meaningful objectname, which the auto slot connection becomes "on_btnBrushColor_clicked()".

Qt Designer just provides default place holder names. It's up to the designer/programmer to provide each of the elements better names. Some of the UI elements cannot be seen or edit while in Designer so editing the *.ui file in a text editor is a good idea. For instance, my "centralWidget" has a QVBoxLayout associated with it that cannot be seen in Designer, so in a text editor I've renamed the non sensible name to "centralWidgetVLayout", and as long as the layout isn't changed to horizontal then the name makes since.

The slots/signals paradigm is new territory for me, but correct me if I'm wrong, wouldn’t better object names make the auto connection less error prone to use? With only a couple days playing around with slots/signals I can't make an informed call.

Still with the ui_MainWindow.h file having that blasted QMetaObject::connectSlotsByName called in setupUi and my partial form at the time of the call, auto connections won't do me much good anyway (still need to look into QAbstractFormBuilder).

Thanks again.

wysota
29th December 2010, 13:12
Yes, "chooseBrushColor" is much better than "on_btn4_clicked". However, renaming "btn4" to "btnBrushColor" makes a much more meaningful objectname, which the auto slot connection becomes "on_btnBrushColor_clicked()".
What if you have two places where you trigger choosing the brush color? You either end up with two identical slots (on_btnBrushColor_clicked and on_SomethingElse_clicked) or you have to manually connect the SomethingElse object to the on_btnBrushColor_clicked slot which completely defeats the purpose of auto-connecting slots and moreover introduces yet more points where your code can break.


Qt Designer just provides default place holder names. It's up to the designer/programmer to provide each of the elements better names.
Designer allows to define custom signal and slot names for the form which makes it possible to make explicit signal/slot connections directly in Designer (by point&click) and then declare implement those slots (and declare signals) in the real QWidget subclass the form is deployed on. That's a much better solution than relying on auto-connect. Search the forum to see how often people end up with double connections by intuitively naming slots in a way that matches the auto-connect scheme and wasting time debugging their apps to find out why slots are triggered multiple times.



The slots/signals paradigm is new territory for me, but correct me if I'm wrong, wouldn’t better object names make the auto connection less error prone to use?
My point of view is that a decent programmer should be totally aware of what her/his code is doing thus all connections should be made explicitly in code. The only exception to this rule is (in my eyes) when you are connecting standard signals and slots between objects that are placed on a form in Designer but providing an ability to "predeclare" slots or signals in Designer was not a good design choice by the Trolls although it does simplify life sometimes (which doesn't change the fact it makes the code more prone to errors). Unfortunately in my opinion you need to have some level of skills to be a software developer and can't rely on some tools to do everything automatically for you. It's ok to use the tools if you really understand how they work and what they do. It's five minutes of work to provide your own scheme of auto-connecting signals and slots if you know what makes Qt tick.

pkohut
29th December 2010, 13:51
What if you have two places where you trigger choosing the brush color? You either end up with two identical slots (on_btnBrushColor_clicked and on_SomethingElse_clicked) or you have to manually connect the SomethingElse object to the on_btnBrushColor_clicked slot which completely defeats the purpose of auto-connecting slots and moreover introduces yet more points where your code can break.
>>snip, a bunch of good points<<


Understood, thanks for the explanation.



My point of view is that a decent programmer should be totally aware of what her/his code is doing thus all connections should be made explicitly in code. ... Unfortunately in my opinion you need to have some level of skills to be a software developer and can't rely on some tools to do everything automatically for you. It's ok to use the tools if you really understand how they work and what they do. It's five minutes of work to provide your own scheme of auto-connecting signals and slots if you know what makes Qt tick.
Excellent points. I always hit a wall with GUI builders where they just get in the way, Qt Designer isn't any different.

wysota
29th December 2010, 18:06
Qt Designer doesn't get in the way if you use it properly. Note that you can obtain the exact same functionality with or without it. It's not that there are some magic tricks that work with a graphical tool and don't work in code or the other way round. It's only important that you understand what Designer does and how it does it. Or rather what uic does and how it does it. And the easiest way to learn that is to look at the code uic generates form an ui file.

pkohut
29th December 2010, 19:30
Have a look at QAbstractFormBuilder, it might give you the flexibility you want, without giving up on the ui files.

This or QFormBuilder looks like it will do the job nicely.