PDA

View Full Version : Connect slot to an int function !?



hakermania
29th December 2010, 15:24
Ok this is quite a noob question, I know. I have made this
constructor of mainwindow.cpp:

(void) new QShortcut(Qt::ALT + Qt::Key_1, this, SLOT(show_m(0)));
(void) new QShortcut(Qt::ALT + Qt::Key_2, this, SLOT(show_m(1)));
(void) new QShortcut(Qt::ALT + Qt::Key_3, this, SLOT(show_m(2)));
(void) new QShortcut(Qt::ALT + Qt::Key_4, this, SLOT(show_m(3)));
well, the slot show_m(int) looks like:


int MainWindow::show_m(int a)
{
ui->tabWidget->setCurrentIndex(a);
}
As you may understand, I want by pressing Alt+1/2/3/4 to navigate through the tabs. I don't get compile error but output error:

Object::connect: No such slot MainWindow::show_m(0) in megauploadMain.cpp:102
Object::connect: (receiver name: 'MainWindow')
Object::connect: No such slot MainWindow::show_m(1) in megauploadMain.cpp:103
Object::connect: (receiver name: 'MainWindow')
Object::connect: No such slot MainWindow::show_m(2) in megauploadMain.cpp:104
Object::connect: (receiver name: 'MainWindow')
Object::connect: No such slot MainWindow::show_m(3) in megauploadMain.cpp:105
Object::connect: (receiver name: 'MainWindow')
Thx in advance for any replies! :)

high_flyer
29th December 2010, 15:30
signal slots take on type parameters not values!

(void) new QShortcut(Qt::ALT + Qt::Key_1, this, SLOT(show_m(int)));


int MainWindow::show_m(int a)
{
switch(a)
{
case 1: //do something
break;
....
}

}

johnc
29th December 2010, 15:41
You can't hard code an input parameter value to a slot on connection. In order to implement what you are trying to do, use a QSignalMapper. You can associate your integer values with a mapping for each shortcut. Something like this:

QSignalMapper* mapper = new QSignalMapper(this);
//Do this for each shortcut, each with different mapping
QShortcut* s = new QShortcut(Qt::ALT + Qt::Key_1, mapper, SLOT(map()));
mapper->setMapping(s, 1);

//Finally, connect the mapper to your classes slot
bool ok = connect(mapper, SIGNAL(mapped(int)), this, SLOT(show_m(int)));
Q_ASSERT(ok); //I always perform this check to cause a debug assertion if I messed something up in the signal connection

high_flyer
29th December 2010, 15:56
johnc is right, but I don't see what use is the signal mapper here.
The signal mapper is good for when you have multiple dynamic, only in run time determined controls that need to be connected to slots by some rules.
But for finite number of known objects, I am not sure if using the a signal mapper is really less coding, or has any other advantage over regular old school connections.
You can implement the same functionality as the signal mapper by just connecting to one slot, and in the slot do:


void MyClass::mySmartSlot()
{
switch(sender())
{
case pButton1: //do button stuff;
break;
case pButton2: // do button 2 stuff;
break;
//or even
case pLineEdit: //we can even do other control stuff with the same slot
break;
}
}


This approach gives far more flexibility, and is not more coding.
But this is usually not needed.

hakermania
29th December 2010, 16:13
Thx both :)

nroberts
29th December 2010, 17:24
johnc is right, but I don't see what use is the signal mapper here.

The benefit of the signal mapper is to avoid violation of LSP. Every time they add a new shortcut/button/whatever to trigger a set_m event they would have to modify their original object to account for it. This isn't the correct way to design. You don't design interfaces to account for the implementation details of each and every client; you design interfaces to be as simple as possible and still complete. That's what the OP has here with set_m(), a function that in turn triggers a specific change within the state of the object.

At the very least you would want to put your switch function in something external to the set_m() object. At this point you're simply reinventing the wheel because QSignalMapper does exactly what is needed.

Code size is often a very poor metric when evaluating solutions. Sometimes it truly is necessary though even in embedded systems this is less and less of a consideration at all. A better metric is something that measures maintainability, and the Principles of OO Design (look them up) are more often than not the best metric I've seen. Still can't really be automated, needs intelligence to evaluate, but are well worth knowing about and using.

high_flyer
29th December 2010, 18:51
The benefit of the signal mapper is to avoid violation of LSP. Every time they add a new shortcut/button/whatever to trigger a set_m event they would have to modify their original object to account for it. This isn't the correct way to design. You don't design interfaces to account for the implementation details of each and every client; you design interfaces to be as simple as possible and still complete. That's what the OP has here with set_m(), a function that in turn triggers a specific change within the state of the object.
All is good an well in what you say - but this is not an interface design - this is and implementation, what we deal here with.
And you will always change the implementation when you change something.
So all the good things you spoke of, as true as they are, do not apply here.

In addition the rest of the things you said are true again, but for large scale proojects, with full production that goes to clients.
This here is a home project.


At the very least you would want to put your switch function in something external to the set_m() object.
The switch function is a downn scaled implementation to what the SignalMapper is doing, but tailored to the specific application.
It could be that it could be put better somehwere else, that was not the point I was making.

Again, SignalMapper has its merits, but also problems.
And they have to be weighed against the "design overhead".
In most "usual" cases, I still say, its not needed, and introduces more problems then it solves.

But, I didn't want to hijack the thread, different people have different opinions, we can agree not to agree :)

wysota
29th December 2010, 19:03
Using QActionGroup is also an option here. Sooner or later you'll probably want to assemble a menu with the options anyway.

squidge
29th December 2010, 19:20
void MyClass::mySmartSlot()
{
switch(sender())
{
case pButton1: //do button stuff;
[...]


When I first learnt about the switch...case statement many years ago, you could only use constant values in case statements, so if you wanted to compare pointer values as above, you had no choice but to use 'if...else' statements, so I've automatically used QSignalMapper in all of the above cases where I have had similar requirements.

Do anyone know when this all changed and the case keyword accepted non-constant values?

wysota
29th December 2010, 19:40
Do anyone know when this all changed and the case keyword accepted non-constant values?
Does it really do that?

nroberts
29th December 2010, 19:40
And you will always change the implementation when you change something.

You're doing it wrong. See the Open Closed Principle.


In addition the rest of the things you said are true again, but for large scale proojects, with full production that goes to clients.
This here is a home project.

Poor design methodology will turn a home project into a total mess just as fast as a large scale project. "It's just home brew," is never a good excuse to cut corners.



The switch function is a downn scaled implementation to what the SignalMapper is doing, but tailored to the specific application.


An unnecessary one. You're missing the point of OO: reuse.



And they have to be weighed against the "design overhead".


And your method has the most. You forgot to measure maintenance in your complexity evaluation. Further you didn't take into account that there now needs to be TWO versions of set_m(), one that depends on there being a signal generator, the other that can be called by client code directly.



Do anyone know when this all changed and the case keyword accepted non-constant values?

It hasn't changed. They could change it to be based on some sort of ID though and I'd be saying the same thing. The pattern itself is flawed, the fact that it doesn't work as specified is just an implementation detail.

wysota
29th December 2010, 19:57
There is another clean solution to the problem. Since QShortcut is a QObject one can do any of these:

QShortcut *sh1 = new QShortcut(...);
sh1->setProperty("tabIndex", 0);
QShortcut *sh2 = new QShortcut(...);
sh2->setProperty("tabIndex", 1);
connect(sh1, SIGNAL(activated()), ..., SLOT(someSlot()));
connect(sh2, SIGNAL(activated()), ..., SLOT(someSlot()));
//...
void ...::someSlot() {
QShortcut *sh = qobject_cast<QShortcut*>(sender());
if(!sh) return;
tabWidget->setCurrentIndex(sh->property("tabIndex").toInt());
}
... or the same but with a pointer, or:

QMap<QObject*,int> map;
//...
map.insert(sh1, 0);
map.insert(sh2, 1);
//...
void ...::someSlot() {
int v = map.value(sender(), -1);
if(v!=-1) tabWidget->setCurrentIndex(v);
}
or again with pointers to widgets...

Clean, reusable, object oriented. The only thing that hurts is the use of sender() but since QSignalMapper and any other solution presented uses it too then that doesn't matter.. I'd probably use QActionGroup anyway.

high_flyer
29th December 2010, 20:34
Do anyone know when this all changed and the case keyword accepted non-constant values?
Sorry - was typing faster then thinking.


You're doing it wrong. See the Open Closed Principle.
Your argumentation is conflicting.
First you said:

Every time they add a new shortcut/button/whatever to trigger a set_m event they would have to modify their original object to account for it.
This is true in any case, unless the connection are done in a loop, with some kind of parametrization.
In addition, by introducing such changes Open Close has been breached, unless, the new additions come in a new derived object, which again, would be "legal" to implement the slot for it.
And then - you say that due to Open Close no such changes should be made, if so, what ever the original implementation was, it should not be changed, be it manually connecting signals, or using SignalMapper.

Don't misunderstand, I am not saying that good design should not be used.
But what I suggested (very crudely - which wysota made much nicer) is that the use of a signal mapper is an implementation detail which can be done differently as well.
Again - for dynamically changing elements that need to be connected at runtime, I think a signal mapper is perfect.



Clean, reusable, object oriented.
Well, almost - in the sense that nrobert is talking about.
If you put the allocation of the QShorcut's in a for loop, and you feed the list of shortcuts from out side, then - it is Open Closes compliant, and in deed reusable while extendable, and if you add new objects all you have to do is implement the new slots for them. (in a derived class of course ;) )

nroberts
29th December 2010, 20:42
Clean, reusable, object oriented. The only thing that hurts is the use of sender() but since QSignalMapper and any other solution presented uses it too then that doesn't matter.

It actually DOES matter, and it matters a great deal. Again you'll need two versions of the function, one that uses sender(), and the other that accepts the index to set. Use of the signal mapper does not impose this pointless dependency on there being a sender upon an object that shouldn't need it. It sticks the stink of that dependency into one place, a class that's sole responsibility is to bind values to senders and forward the signal.

It may seem like a pedantic issue to some but it's decisions just like this, about seemingly little things, that can make or break a project. First and foremost on any developer/designer's mind should be to NEVER create unnecessary dependencies. Your proposal does.

wysota
29th December 2010, 22:00
It actually DOES matter, and it matters a great deal. Again you'll need two versions of the function, one that uses sender(), and the other that accepts the index to set.
The code I presented is complete, there is just one slot and one function call.


Use of the signal mapper does not impose this pointless dependency on there being a sender upon an object that shouldn't need it. It sticks the stink of that dependency into one place, a class that's sole responsibility is to bind values to senders and forward the signal.
QSignalMapper uses sender() itself so that's just changing the point where it is used. Unfortunately the solution with signal mapper fails immediately when you insert a widget in the middle of the stacked widget. Using QList or QActionGroup doesn't contain this flaw. And it doesn't impose any additional dependencies since QSignalMapper also uses a container to store a list of mappings. My solution can easily be adapted to use a list instead of a map to avoid a problem when pages are dynamically added into the stack.
The fact where you do the encapsulation is meaningless.


It actually DOES matter, and it matters a great deal. Again you'll need two versions of the function, one that uses sender(), and the other that accepts the index to set.
The code I presented is complete, there is just one slot and one function call.


Use of the signal mapper does not impose this pointless dependency on there being a sender upon an object that shouldn't need it. It sticks the stink of that dependency into one place, a class that's sole responsibility is to bind values to senders and forward the signal.
QSignalMapper uses sender() itself so that's just changing the point where it is used. Unfortunately the solution with signal mapper fails immediately when you insert a widget in the middle of the stacked widget. Using QList or QActionGroup doesn't contain this flaw. And it doesn't impose any additional dependencies since QSignalMapper also uses a container to store a list of mappings. My solution can easily be adapted to use a list instead of a map to avoid a problem when pages are dynamically added into the stack.
The fact where you do the encapsulation is meaningless.

nroberts
29th December 2010, 22:47
Your argumentation is conflicting.


No, it isn't. You haven't understood it.

You quote me as saying, "Every time they add a new shortcut/button/whatever to trigger a set_m event they would have to modify their original object to account for it."

This is a statement was directed against your version. It is true that a violation of LSP is always also a violation of OCP but it can be easier to see it as LSP violation immediately. The "original object" here is that class that has the set_m() function.



This is true in any case, unless the connection are done in a loop, with some kind of parametrization.


No, this is not at all true in any case. Your suggestion uses particular button pointers (and lets just assume for a moment that this actually works) to refer to indexes and this is hard-coded into your version of set_m(). In order to connect a new button to your object you end up having to add additional cases within set_m(). This violates OCP.

In the original version the OP can attach as many signal generating objects as they want, and even call the function directly without any signal, without ever changing set_m() at all. All they have to do is attach an integral value (the index) with the signal that's being popped and link that to the set_m() function/slot. This is *extension* and is NOT a violation of OCP.



And then - you say that due to Open Close no such changes should be made, if so, what ever the original implementation was, it should not be changed, be it manually connecting signals, or using SignalMapper.


And as you can see, by using the SignalMapper no such change is made. The only changes made are in client code, which does little more than assemble a collection of objects, which as it should be. You want your changes to consist of nothing more than extension and reassembly.



Don't misunderstand, I am not saying that good design should not be used.
But what I suggested (very crudely - which wysota made much nicer) is that the use of a signal mapper is an implementation detail which can be done differently as well.


This is sort of true. Use of the SignalMapper is just an implementation detail. What it does and how it does it could be done a variety of different ways and the SetM object wouldn't care or know. Where to put that stuff though is not "just" an implementation detail for where both of you are proposing to place it imposes coupling that needn't and therefore shouldn't exist. Any amount of signal remapping methodologies could be applied in both this case and others. Good design dictates that you don't tie the SetM object to any one of them, nor its clients; you stick these details in separate objects that deal with this issue and this issue alone, deferring the details of what to do about the mapping that's created to other objects.



Again - for dynamically changing elements that need to be connected at runtime, I think a signal mapper is perfect.


It does exactly what it's made to do, and perhaps that's what the OP needs and perhaps it is not. What is certain that they don't want is to put the details of this decision in the object that set_m() is a part of.



The code I presented is complete, there is just one slot and one function call.


Your version is NOT complete in comparison to the OP's object. The original set_m() interface accepted an index parameter. It could therefor be called in a great variety of ways that neither of your methods can fully account for. The former fails whenever you want to connect to something besides a shortcut. The latter fails when you wish to connect the signal to an object that can generate the indexes itself. Both fail when you wish to simply pragmatically (ie, not in a signal) change the index.

Leaving the OP's object alone, as we should, and implementing the mapping magic in an external object such as SignalMapper, is exactly the thing that will both get the OP what they want and leave their class uncoupled to the implementation details of that mapping. It will allow them a greater variety of extensions in the future, all without having to change one single line of anything within the object they're attempting to control.

Whether or not QSignalMapper is the appropriate answer, it does seem to be in this case, is beside the point. The point is to NOT tie the set_m() object to anything in particular, allowing for the greatest amount of reuse and maintainability both for this object AND for the mapping functionality.



The fact where you do the encapsulation is meaningless.


I should hope you are blushing now. WHERE to do encapsulation is exactly the point of everything in object oriented and generic programming. If it was meaningless where to do encapsulation then encapsulation itself would be completely meaningless and redundant. The whole idea of OO would be absurd.

Yet another way to examine why both of you are in error is to look at the Single Responsibility Principle. This states that any object, function, or construct, should have a single and solitary purpose.

The OP has an object that governs something, and it doesn't matter what, that is indexed. That is all it should do. The set_m(int) function is related to this purpose because it changes the current index in that model. It does one thing and one thing only: it changes that current index. So far everything is the way it should be.

Now both of you are suggesting that the user also embed widget mapping into the things that the set_m() function does. We know that this isn't a correct responsibility for this function to have because the index of this model has been shown to us as not being particular UI elements. At the very least the mapping of widget to index should not be within the set_m() function, who's sole responsibility is to change the current index.

The question that remains at that point is whether or not the mapping function belongs in the object at all. Given the information we've been given by the OP we have no reason to believe that this is the case. If they had told us the intention of the object is to provide a model that can be indexed by UI element then the story would be different, but we haven't been told that. Even if we where we'd have to question the intent since this is a Newbie channel.

Truth is we have 3 clearly distinct types of responsibility here:

1) Respond to user input and generate signals.
2) Map signals/senders into an index
3) Manage an indexed whatever.

All three can be turned into generic abstractions for reuse in other scenarios and in fact, until you guys decided to couple 2&3, two of these things where already abstracted. The only one remaining was the mapper and there's at least one that exists and can do some things already.

Again, violation of the SRP is violation of OCP for when you tie 2&3 together then if you wish to change the behavior of the program you are no longer simply assembling a different collection of objects, you have to alter one object from the perspective of one or both of two directions. While keeping them separate allows you to simply replace THE part that needs to behave differently.

hakermania
29th December 2010, 23:01
Omg :) now I can understand the power of a simple question ;)

nroberts
29th December 2010, 23:12
The "original object" here is that class that has the set_m() function.

You might be asking at this point why it matters WHERE the change is happening and what KIND of change is required. For example, why is it better to subclass an interface to get new behavior rather than alter an existing one.

This is sort of why I suggested looking these things up. It's a long discussion and at this point I have to assume certain premises in order to reasonably critique design decisions.

What it boils down to though is that experience shows that some types of changes are easier than others. It is easier to write new code than to maintain legacy code. People everywhere who have to maintain long existing projects lament at the task of altering existing code. Every time you do it you have to make sure you haven't broken anything, etc...etc... You have to learn WTF the person who wrote it was thinking (even when it's yourself and you only did it two weeks ago, not 10 years). This is true because the human brain is simply incapable of tracking the enormous complexity of even the simplest programs; it can only do so in layers and abstractions. Thus it's always easier just to write new code, so the object is to make that what you are doing as much as possible.

So, years and years of experts shooting themselves in the face over and over and over again, and finally figuring out some semblance of a system of criteria that works has resulted in these principles. It's well worth becoming familiar with them and getting used to thinking about them as you write and critique your own designs. Unfortunately, though this is probably the single most important skill for a developer to have...I don't know of any school that teaches it.

It's a cost benefit always. You can't be open to all extensions and closed to all changes. Sometimes you just didn't account for what the user wanted and you are forced to change something. That doesn't negate the principle though and in the case when you CAN see it, and can easily solve the problem without closing those paths of extension, you nearly always should (I'm tempted to omit 'nearly').

Of course, a newbie is not going to get it right in the beginning. Just like everything else. I would say though that these principles and issues should really be TOP on the concerns of all new developers to figuring out and becoming proficient in.


Omg :) now I can understand the power of a simple question ;)

I wouldn't worry if you didn't understand everything I said. The only thing I would worry about if I were you is two things:

1) if you can get the behavior you want without having to change set_m, do it that way.

2) Go search the web for "Principles of Object Oriented Design", "Robert C. Martin", and "Open/Closed Principle" and just keep reading about that stuff over, and over, and over...for the rest of your career. It'll sink in in parts, just like coding in any particular language/API, and you'll be a cut above a LOT of people in the field.

wysota
29th December 2010, 23:12
Your version is NOT complete in comparison to the OP's object.

(...)

I should hope you are blushing now. WHERE to do encapsulation is exactly the point of everything in object oriented and generic programming.
The point is to have the encapsulation in the first place. And it is you who should be blushing if you suggest using an external signal mapper object as a good design. Read your own posts again and ask yourself whether an external signal mapper object is not a dependency here and is it the right encapsulation type and also think about all the other stuff you have written. It's nice that you know the rules but you are violating them yourself. If you want to use a signal mapper, it should be part of a solid black-box object only exposing means to register a shortcut to change the current index of a tab-widget (or whatever the component is) either through a has-a or a is-a relation. As for your first argument QTabWidget and QStackedWidget already contain a slot taking an int and changing the current index so there is no need in duplicating it and the slot I have shown should be a private one (ignoring for a moment that there are no such things as private slots) to make sure it only gets called in a valid situation by a valid object. And preguessing your next argument that the slot might be doing something more than just changing indexes I will say that if you want to do more then you should connect to a signal emitted when a current index is changed to be sure the behaviour stays consistent regardless of what causes the current index to change. If you want more complex semantics then it's still best to wrap it around the infrastructure for signalling index changes.

I don't (and neither does Daniel) say using a signal mapper is a bad approach here. I say that the sole fact of using a signal mapper doesn't really solve the problem especially since I pointed out a situation where it fails completely when used as an external object hence making the whole "architecture" completely unreliable and unreusable in an Open World. Sure, in some situations it will work, as will Dani's solution and a bunch of others so I don't see why we should value one over another. Moreover I say that if you wrap (i.e. subclass) the widget container class to establish a separate (internal) list (or map) of dynamically maintained mappings, this will be the ultimate solution to the problem. In the end you don't really care whether some shortcut maps to "1" or "7", you only care that a particular widget pops up so this is what the interface should expose, the numbers are just a technical detail.

nroberts
29th December 2010, 23:36
The point is to have the encapsulation in the first place.

Sigh...this is bad. Really bad. Just applying encapsulation willy-nilly is just not the way to do things. I shouldn't have to convince an "expert" of this fact but I suppose anyone stepping into your trap will figure it out quickly enough.



And it is you who should be blushing if you suggest using an external signal mapper object as a good design. Read your own posts again and ask yourself whether an external signal mapper object is not a dependency here and is it the right encapsulation type and also think about all the other stuff you have written. It's nice that you know the rules but you are violating them yourself.


I actually supplied a lot of reasoned explanation and all you say is, "You're violating them." You'll need to point out HOW or I'm just not buying.

You ask, is using an external mapper a good design?

I explained quite clearly that it depends and that at this point, given what I know of the OP's requirements...YES! But lets say I'm totally wrong about what the user needs and in fact they really do wish to directly map particular UI elements to indexes in their model (as in they seriously wish to couple the model's index to particular UI elements and there is no other way to index whatever they're tracking). Would I change my story?

Answer: NO! Why? One word: composition. The fact that mapping from one thing to another is a specific and unique responsibility has not changed. Put that stuff into an object responsible for just that and then USE that object within the object you want to couple it to.

You ask whether an external signal mapper object is not a dependency here.

Well, unless you can show me where I'm wrong my answer is a very certain NO! Why? Because NONE of the objects in question depend upon it!

Does the set_m(int) object depend on it? No. Anything that calls set_m with an integer is a valid client.

Does the object sending the signal depend on it? NO! It can be connected to anything which can observe the signals it emits.

Does the mapper itself depend on either the signal generator or the set_m(int) object? No! It can be connected as a receiver to any object and then emit a signal to any object upon which that mapping is useful.

Does the set_m(int) object depend on the button or UI element that generates the original signal? No! It doesn't care who requests an index change. Only if it's done your way to we see coupling in this direction.

Does the button depend on the set_m(int) object? No. It just pops signals to anyone that will listen.

So no, I don't see any unnecessary coupling or dependencies here. Most of the objects don't even know that the others exist and don't even care.

There are only two entities that depend on the mapping: the mapper itself and the code that created it. In this case, since we're not recommending a more complex but open design that uses a mapper interface and factory, this later part is the function that connected the three objects together. This is a very small, localized part of code that is hopelessly coupled because it simply HAS to be.

...snip a bunch of "should" "should" "should" without any reasoning behind it... <- it's all rather beside the point anyway (and please use some semblance of paragraph structure!)



I say that the sole fact of using a signal mapper doesn't really solve the problem especially since I pointed out a situation where it fails completely when used as an external object hence making the whole "architecture" completely unreliable and unreusable in an Open World.

You'll have to point out where you showed it wouldn't work because I not only missed it the first time but can't find it when I try to backtrack. In fact, I can only see where you claimed it didn't matter.

I don't know who "Daniel" is, but if it's "high_flyer" then I really suggest you reevaluate his proposed solution. It's not even valid C++, as has been pointed out already.

wysota
30th December 2010, 00:30
Sigh...this is bad. Really bad. Just applying encapsulation willy-nilly is just not the way to do things.
Good thing I didn't say anything like that.


Answer: NO! Why? One word: composition. The fact that mapping from one thing to another is a specific and unique responsibility has not changed. Put that stuff into an object responsible for just that and then USE that object within the object you want to couple it to.
I don't see how using a signal mapper is in any way better than any of the other presented designs.


Well, unless you can show me where I'm wrong my answer is a very certain NO! Why? Because NONE of the objects in question depend upon it!
Objects don't depend on it, the overall functionality does. The sole fact that you need to create this object in addition to the objects you really need makes it a dependency. It's just an adapter between two data types but as such the functionality of the two incompatible interfaces depends on existance of this object. Existance of the show_m slot is meaningless to the whole problem, the method itself is useful without appointing shortcuts to widgets. The whole point is to extend the infrastructure to accept such shortcuts and not to map indices. Sure you can add several adapters doing nothing more than mapping between types or brewing coffee but that doesn't change the fact they won't make the original object accept shortcuts to popping up widgets.


You'll have to point out where you showed it wouldn't work because I not only missed it the first time but can't find it when I try to backtrack. In fact, I can only see where you claimed it didn't matter.
Geeez man...


tabWidget->addTab(new QCalendarWidget);
tabWidget->addTab(new QDial);
QSignalMapper *mapper = ...;
QShortcut *sh1 = ...; // to popup the calendar
QShortcut *sh2 = ... // to popup the dial
mapper->setMapping(sh1, 0);
mapper->setMapping(sh2, 1);
connect(sh1, SIGNAL(activated()), mapper, SLOT(map()));
connect(mapper, SIGNAL(mapped(int)), tabWidget, SLOT(setCurrentIndex(int)));

// some other scope, maybe written by some other person...
// this breaks the design:
tabWidget->insertTab(0, new QWidget); // now sh1 points to the QWidget and sh2 points to the calendar, oops...

Now you can't possibly break this unless you really try it (WARNING, pseudocode!):

class TabWidgetWithShortcuts : public QTabWidget {
Q_OBJECT
public:
TWWS(...) : QTW(...) {}
void registerShortcut(int page, QShortcut *sh) {
if(page not in [0, count()-1] ) return;
m_list[page] = sh;
connect(sh, SIGNAL(destroyed(QObject*)), this, SLOT(_q_onShDestroyed(QObject*)));
connect(sh, SIGNAL(activated()), this, SLOT(_q_activated()));
emit shortcutRegistered(page);
}
void unregisterShortcut(int page) {
if(!m_list.at(page)) return;
disconnect(m_list.at(page), SIGNAL(destroyed(QObject*)), this, SLOT(_q_onShDestroyed(QObject*)));
disconnect(sh, SIGNAL(activated()), this, SLOT(_q_activated()));
emit shortcutUnregistered(page);
m_list[page] = 0;
}
QShortcut *shortcut(int page) const {
// check bounds, etc.
return m_list.at(page);
}
protected:
void tabInserted(int at) {
m_list.insert(at,0);
}
void tabRemoved(int at) {
unregisterShortcut(at);
m_list.removeAt(at);
}
private:
QList<QShortcut*> m_list;
private slots:
void _q_onShDestroyed(QObject *o) {
// remove shortcut from list
}
void _q_activated() {
QShortcut *sh = qobject_cast<QShortcut*>(sender());
if(!sh) return;
setCurrentIndex(m_list.indexOf(sh));
}
signals:
void shortcutRegistered(int);
void shortcutUnregistered(int);
}

Go ahead, be picky... The hell with it, I'll add some signals to it, it's almost compilable anyway. Look maa... no signal mappers!

By the way, you can do the same with QActionGroup without the dedicated list as QActionGroup already has one embedded.


I don't know who "Daniel" is, but if it's "high_flyer" then I really suggest you reevaluate his proposed solution. It's not even valid C++, as has been pointed out already.
The idea matters, correctness of the code is exercised against a compiler. I'm sure you make syntax errors in your code too as we all do. If you want to find a flaw you will always find it in anything and at the same time you'll miss the bigger picture.

nroberts
30th December 2010, 01:02
Good thing I didn't say anything like that.

You very much did: "...where you do the encapsulation is meaningless."



I don't see how using a signal mapper is in any way better than any of the other presented designs.


And yet you've countered none of my arguments.



Objects don't depend on it, the overall functionality does. The sole fact that you need to create this object in addition to the objects you really need makes it a dependency.

I'm sorry but I just can't make it past this point. I'm talking over your head or something because this statement just proves you don't get it. You don't even know what I'm talking about when I say the word "dependency".

This isn't me trying to be rude. It's just a sad truth. When I'm using the word "dependency" in the context of design decisions I'm using the word in a very specific, technical way. The fact that a certain behavior is desired and must be implemented somewhere does not create dependencies. In the language of program design, "dependencies" exist when one "entity" is in some way bound to the existence of another...in that it requires it to exist.

Some types of dependencies that can exist in a design are dependency to an interface, dependency of a name, etc... For example, any entity that contains the statement, "new Object," depends on the name "Object" and anything necessary to build it. An entity that contains a line of code like, "instance->doX()," depends on the name "instance", the identity of what instance IS, and the member of that interface, doX().

The signal/slot mechanism in Qt removes dependencies like this. Say I have two objects:



struct X : QObject
{
Q_OBJECT

signals:
void sig();
};

struct Y : QObject
{
Q_OBJECT

public slots:
void s();
};


These two objects exist within the same program. They perform their specific functions within the behavior of that program. However, as you can see, there are absolutely no dependencies between the two. I could remove either one and use it, unchanged, in another program that doesn't have the other class.

They do both depend on QObject, and further they both depend on and obey that interface created by the MOC when the Q_OBJECT macro is used. Because they do this, and because of the way the QObject interface works, I can connect these two otherwise unrelated objects together to generate new behavior.

This kind of thing, having two objects that you wish to use together remain independent of each other by binding both to a higher abstraction, is called "Dependency Inversion" BTW.

So, say we add a user requirement that means I need new behavior that must be thrown into the mix here. The addition of a new class does not create a dependency on that class to these other two if I don't cause them to be bound to the existence of that new object. Yes, the behavior of the program depends on the existence of code, somewhere, that does what the new object does. It may even require that this code happen sometime between the emitting of X's signal and Y's slot. X and Y, and this new object (Z) do not depend on each other though. I could remove any of them, put them together in different ways or not at all, and generate new behavior in new programs...or even within the SAME program....and here's the important part that is WHY we try to remove dependencies: I don't have to change any of them to do so.

Another very important aspect of this is that I can add Z between X and Y only BECAUSE they do not depend on each other.

THAT is what developers and designers are talking about when they say, "dependency" and there are many levels of abstraction upon which we use the term. At the component level, at the application level (X.exe depends on Y.exe), and also at the class level and lower.

Of course, I have a feeling that you already know all of this and are just using the same old, "look over HERE!!!," method of argument where you distract your opponent and readers by switching between the meanings of words. For instance, when arguing about the temperature of water someone might say, "It's cold," and then you'd respond with, "No, it's nose isn't running. It doesn't even have one." Yes, we could say that the behavior of the program depends on the existence of the code I'm proposing to encapsulate within a smaller object instead of embed into another that's already doing other things. We could say that but we both know that the discussion is about dependency in DESIGN.

I did skim the rest of your stuff....don't see anything new. I have to be honest in not being very interested though because clearly you either don't understand the language of program designers or you're up to dishonesty. Either way I'm not particularly interested in "discussing" with someone that is not interested in curing their ignorance any more than "discussing" with someone that only wants to appear right, no matter what it takes.

wysota
30th December 2010, 11:54
And yet you've countered none of my arguments.
The purpose of me being here is something more than countering your arguments. Note that I didn't comment on your opinions in the thread until you started commenting mine. I just don't see why you are defending the approach so much and trying to prove it is superior to some other approach when clearly they all suffer from the same problems.


I'm sorry but I just can't make it past this point. I'm talking over your head or something because this statement just proves you don't get it. You don't even know what I'm talking about when I say the word "dependency".
Maybe. But who said the word "dependency" has only a meaning when talking about objects? If you want a more scientific expression I can say that the signal mapper is tightly coupled with the tab widget if it is to provide the functionality desired. Otherwise if the tab widget ignores the presence of the signal mapper, you get the problem which I demonstrated earlier and which you gladly ignored. You can either see a context of using some objects or not. It seems you are focusing solely on syntax without taking a broader look at the context. It's ok, it's your will but don't say others are wrong only if they look at the situation from a broader perspective.


This isn't me trying to be rude. It's just a sad truth.
That is to be judged by each person subjectively.


Of course, I have a feeling that you already know all of this and are just using the same old, "look over HERE!!!," method of argument where you distract your opponent and readers by switching between the meanings of words.
Right. That's the real purpose for this site being here. So that one can distract others.


I did skim the rest of your stuff....don't see anything new.
Too bad you just skimmed it but I can understand it's hard for you to say this design was indeed better than what you had suggested. There is nothing new there, it's just a correct approach to solving the problem. It doesn't rely on any external objects, the encapsulation is correct, it does its job and it's secure. Which can't be said about a unilateral relationship between an external signal mapper and the widget container.


I have to be honest in not being very interested though because clearly you either don't understand the language of program designers or you're up to dishonesty. Either way I'm not particularly interested in "discussing" with someone that is not interested in curing their ignorance any more than "discussing" with someone that only wants to appear right, no matter what it takes.
You really have a high self-esteem, don't you? You think your solutions are correct ones because they are yours and you can't find a flaw in them and everything else somebody else suggests just has to be wrong simply because your approach is by definition *the best*. And when you run out of arguments, you start making the discussion personal. What's the purpose, I ask you?

high_flyer
30th December 2010, 12:08
@nroberts:

ither way I'm not particularly interested in "discussing" with someone that is not interested in curing their ignorance any more than "discussing" with someone that only wants to appear right, no matter what it takes.
you should read you own words, and act on it, especially the last part of the sentance.
You ignorance may not be so much in the technical issues, which clearly you seem to know quite a bit about, but about conduct, and proper manner in discourse.
That too, is an important skill, and it seems you ignorance in that department, is larger than the your technical knowledge.
Maybe its not ignorance, it seems more likes its exactly what is in the sentence of yours that I quoted.
Too bad really, since I think your non emotional contribution to the thread was good, and interesting.

May we close this thread now please?
It brings no new value to anyone anymore.