Qt-interest Archive, March 2008
templated functions as SLOTs?
Message 1 in thread
I have a design problem which IMHO is quite interesting. Maybe someone
can help me with it.
In my application I have dozens of menu entries, which all do the same
thing: they create an object, after a couple of checks. I have defined
slots for each of them, and they all look like this:
void MyWidget::slot1() {
Class1 *myObject;
// tests galore...
myObject = new Class1;
// some more tests...
}
The menu entries are, basically, created like this:
menu->addAction("slot1", this, SLOT(slot1()));
Now this leads to a lot of boring, repetitive, hard to maintain and ugly
code. What I want ideally (and of course I know this isn't possible in
C++), is this:
template<class my_class> void MyWidget::slot() {
my_class *myObject;
//...
myObject = new my_class;
//...
}
menu->addAction("slot1", this, SLOT(slot<Class1>()));
I have found a solution, but it is unsatisfactory. I'm not going to
describe it in depth (no need to spam the list with unsatisfactory
solutions ;-). The gist of it is that I created a class template with a
static member function doing the work required in every slot, and called
the appropriate template from every slot. Removed some bloat, but added
new problems (in particular, I have to declare every template
instantiation as a friend class of MyWidget), and I still have to
declare and maintain dozens of slots.
Has anyone found an elegant solution for this problem?
Greets, Helge
--
[ signature omitted ]
Message 2 in thread
On Friday March 14 2008 14:12:32 Helge Preuss wrote:
> In my application I have dozens of menu entries, which all do the same
> thing: they create an object, after a couple of checks. I have defined
> slots for each of them, and they all look like this:
How about using just one slot, and then instantiate the object based on the
callee? You could then either do that as a long switch on some id or name or
something, or you could make a self-registering scheme to handle it
automatically. If you don't like to use to callee, you can probably whip
something up with QSignalMapper to eliminate that part.
--
[ signature omitted ]
Message 3 in thread
On Friday March 14 2008, Esben Mose Hansen wrote:
> How about using just one slot, and then instantiate the object based on the
> callee? You could then either do that as a long switch on some id or name or
> something, or you could make a self-registering scheme to handle it
> automatically.
Hi, I'm pretty new to Qt, and started some basic coding with it last month.
As an example I was trying to build a small app, that brought me in a similar situation.
Several buttons would send a clicked() singal, and calling all the same function
(and as argument the name of the field that the function should update).
It turned out that such a signal/slot connection didn't work, all I got was
"Object::connect: No such slot StatGenScreen::vectorToFld(&fld_CON)"
(I tried with references, pointers, but none work.
So I'm pretty much interested to know how to reference the callee inside the slot function, and make the decisions from within the function.
Any ideas?
Best regards,
Christopher Ranschaert (Qt - Newbie)
--
[ signature omitted ]
Message 4 in thread
"Christopher Ranschaert" <haploc-ml@xxxxxxxxx> wrote in message
news:200803141538.35707.haploc-ml@xxxxxxxxxxxx
> On Friday March 14 2008, Esben Mose Hansen wrote:
>> How about using just one slot, and then instantiate the object based on
>> the
>> callee? You could then either do that as a long switch on some id or name
>> or
>> something, or you could make a self-registering scheme to handle it
>> automatically.
>
> Hi, I'm pretty new to Qt, and started some basic coding with it last
> month.
> As an example I was trying to build a small app, that brought me in a
> similar situation.
> Several buttons would send a clicked() singal, and calling all the same
> function
> (and as argument the name of the field that the function should update).
> It turned out that such a signal/slot connection didn't work, all I got
> was
>
> "Object::connect: No such slot StatGenScreen::vectorToFld(&fld_CON)"
> (I tried with references, pointers, but none work.
>
> So I'm pretty much interested to know how to reference the callee inside
> the slot function, and make the decisions from within the function.
>
> Any ideas?
That is an often made mistake, trying to add values for parameters in the
connect statement. That's not the way to do it. There are two options, both
already pointed out by Esben Mose Hansen:
1) Use QObject::sender() to find which other QObject send the signal that
triggered the slot you are processing. If you ask me, it's not pretty, as it
breaks the separation of code. But, it can be usefull sometimes.
2) Use QSignalMapper. It works as a proxy between QObjects you would
otherwise connect directly (like the buttons and your form). QSignalMapper
allows you do something like this:
Button1::SIGNAL(clicked()) -> QSignalMapper::map() ->
QSignalMapper::SIGNAL(mapped(1)) -> Form::SLOT(doStuff(int));
You can connect many signal sources this way, and give them all a unique
identifier you wish your doStuff slot to receive.
André
--
[ signature omitted ]
Message 5 in thread
On Friday 14 March 2008 16:03:28 André Somers wrote:
> That is an often made mistake, trying to add values for parameters in the
> connect statement. That's not the way to do it. There are two options, both
> already pointed out by Esben Mose Hansen:
Qxt::bind can do that :P
http://docs.libqxt.org/classQxtMetaObject.html#1c3ae4a2ab8be48b311706e37d0a550df
--
[ signature omitted ]
Message 6 in thread
On Friday March 14 2008, André Somers wrote:
>
> "Christopher Ranschaert" <haploc-ml@xxxxxxxxx> wrote in message
> news:200803141538.35707.haploc-ml@xxxxxxxxxxxx
> >
> > So I'm pretty much interested to know how to reference the callee inside
> > the slot function, and make the decisions from within the function.
> >
> > Any ideas?
>
> That is an often made mistake, trying to add values for parameters in the
> connect statement. That's not the way to do it. There are two options, both
> already pointed out by Esben Mose Hansen:
> 1) Use QObject::sender() to find which other QObject send the signal that
> triggered the slot you are processing. If you ask me, it's not pretty, as it
> breaks the separation of code. But, it can be usefull sometimes.
> 2) Use QSignalMapper. It works as a proxy between QObjects you would
> otherwise connect directly (like the buttons and your form). QSignalMapper
> allows you do something like this:
>
> Button1::SIGNAL(clicked()) -> QSignalMapper::map() ->
> QSignalMapper::SIGNAL(mapped(1)) -> Form::SLOT(doStuff(int));
>
> You can connect many signal sources this way, and give them all a unique
> identifier you wish your doStuff slot to receive.
Many thanks, Andre, for pushing me in the right direction.
I had found on several spots that my idea of slots, and giving along those parameters is not the way.
As I am pretty much a beginner with Qt, I didn't really have an idea where to look for alternatives.
I'll try to implement it when I find some time on the way home (train).
Thanks again!
Christopher Ranschaert.
--
[ signature omitted ]
Message 7 in thread
Esben Mose Hansen wrote:
>> In my application I have dozens of menu entries, which all do the same
>> thing: they create an object, after a couple of checks. I have defined
>> slots for each of them, and they all look like this:
>>
> How about using just one slot, and then instantiate the object based on the
> callee?
Does Qt provide any support for that? I can't think of a way to
determine the caller from a slot by default.
Of course I could do
class MyWidget {
// ...
public slot:
void slot (QString &);
}
But how would I create the corresponding QAction?
menu->addAction("slot1-title", this, SLOT(slot("slot1"));
does not look right to me. OTOH, all the signal/slot stuff is handled by
the preprocessor, so it might just work out.
The point I don't like about it is, templates are type safe. If I use a
string instead to decide what action to take, all that nice type safety
goes down the drain.
> You could then either do that as a long switch on some id or name or
> something, or you could make a self-registering scheme to handle it
> automatically. If you don't like to use to callee, you can probably whip
> something up with QSignalMapper to eliminate that part.
>
I'd have to look deeper into QSignalMapper, never heard of it before.
Thanks.
--
[ signature omitted ]
Message 8 in thread
On fredag den 14. Marts 2008, Helge Preuss wrote:
> Esben Mose Hansen wrote:
> >> In my application I have dozens of menu entries, which all do the same
> >> thing: they create an object, after a couple of checks. I have defined
> >> slots for each of them, and they all look like this:
> >>
> >
> > How about using just one slot, and then instantiate the object based on
> > the callee?
>
> Does Qt provide any support for that? I can't think of a way to
> determine the caller from a slot by default.
http://doc.trolltech.com/4.3/qobject.html#sender
Bo.
--
[ signature omitted ]
Message 9 in thread
Here's what I came up with over lunch (hooray for the creative potential
of lunch breaks!). I haven't tested it yet, because I have other things
to do ATM besides seeking elegant solutions, but I wanted to pin it down
now.
class Base { };
class Class1: public Base { };
// more descendants of Base...
template<class T> class SlotHelper {
Q_OBJECT
public:
SlotHelper(MyWidget *w): myWidget(w) {}
public slots:
void slot() {
// tests galore...
myWidget->myObject = new T;
// some more tests...
}
private:
MyWidget *myWidget;
};
class MyWidget: public QWidget {
friend class SlotHelper<Class1>;
// more friend declarations...
public:
void generateMenu();
Base *myObject;
QMenu *menu;
};
void MyWidget::generateMenu() {
menu->addAction("slot1-title", new SlotHelper<Class1>(this),
SLOT(slot()));
// etc.
}
Remaining problems:
- I save all the slot declarations in MyWidget, but I have to pay for
them with one forward class declaration and one friend declaration for
every slot declaration saved. Still, this overhead goes only into the
header file, not the implementation.
- I'm not sure if calling addAction with an anonymous pointer to a
freshly generated SlotHelper<Class1> creates problems?
--
[ signature omitted ]
Message 10 in thread
Helge Preuss wrote:
> template<class T> class SlotHelper {
> Q_OBJECT
> public:
> SlotHelper(MyWidget *w): myWidget(w) {}
> public slots:
> void slot() {
> // tests galore...
> myWidget->myObject = new T;
> // some more tests...
> }
> private:
> MyWidget *myWidget;
> };
Now that I tried it, I see that Qt does not support this approach. moc
throws an error: "Template classes not supported by Q_OBJECT". That's a
pity.
I have saved over 500 lines of code on the way to this discovery though.
Factored out the common code in all the slots into a static function in
a class template. That's not bad. I think I'll call it a day.
--
[ signature omitted ]
Message 11 in thread
I hope this helps:
No friend class, and working slots (as far as I know).
class Base;
class ClassMaker
{
public:
virtual Base* make_base()=0;
};
template <typename T>
class TemplatedClassMaker: public ClassMaker
{
public:
virtual Base* make_base()
{
return new T;
}
};
class Base
{ };
class Class1: public Base
{ };
// more descendants of Base...
class SlotHelper {
Q_OBJECT
public:
SlotHelper(MyWidget* w,ClassMaker *cm): mw(w),class_maker(cm) {}
~SlotHelper() { delete class_maker;}
public slots:
void slot() {
// tests galore...
mw->addBase(class_maker->make_base());
// some more tests...
}
private:
MyWidget* mw;
ClassMaker *class_maker;
};
class MyWidget: public QWidget {
public:
void addBase(Base* b)
{ myObject=b; }
void generateMenu();
Base *myObject;
QMenu *menu;
};
void MyWidget::generateMenu() {
menu->addAction("slot1-title", new SlotHelper(this,
new TemplatedClassMaker<Class1>() ),SLOT(slot()));
// etc.
}
--
[ signature omitted ]
Message 12 in thread
If you are using a QActionGroup that is common to all those actions
and only one can be checked at any time, you can call
your_action->setData( QVariant your_variant )
on each action when you create them, and then call
your_action_group->checkedAction()
in the slot.
I'm not sure if your situation is similar, but imagine you have a menu
to choose a language. You can set your application language with one
single slot that checks the data stored in the only active action (a
string with the language code, for instance) and then does the
appropriate calls based on that.
On Fri, Mar 14, 2008 at 2:12 PM, Helge Preuss <helge.preuss@xxxxxxx> wrote:
> I have a design problem which IMHO is quite interesting. Maybe someone
> can help me with it.
>
> In my application I have dozens of menu entries, which all do the same
> thing: they create an object, after a couple of checks. I have defined
> slots for each of them, and they all look like this:
>
> void MyWidget::slot1() {
> Class1 *myObject;
>
> // tests galore...
>
> myObject = new Class1;
>
> // some more tests...
> }
>
> The menu entries are, basically, created like this:
>
> menu->addAction("slot1", this, SLOT(slot1()));
>
> Now this leads to a lot of boring, repetitive, hard to maintain and ugly
> code. What I want ideally (and of course I know this isn't possible in
> C++), is this:
>
> template<class my_class> void MyWidget::slot() {
> my_class *myObject;
> //...
> myObject = new my_class;
> //...
> }
> menu->addAction("slot1", this, SLOT(slot<Class1>()));
>
> I have found a solution, but it is unsatisfactory. I'm not going to
> describe it in depth (no need to spam the list with unsatisfactory
> solutions ;-). The gist of it is that I created a class template with a
> static member function doing the work required in every slot, and called
> the appropriate template from every slot. Removed some bloat, but added
> new problems (in particular, I have to declare every template
> instantiation as a friend class of MyWidget), and I still have to
> declare and maintain dozens of slots.
>
> Has anyone found an elegant solution for this problem?
>
> Greets, Helge
>
> --
> Helge Preuss
> Freelance Software Developer
> +49 177 2262 484
> helge.preuss@xxxxxxx
>
> --
> To unsubscribe - send a mail to qt-interest-request@xxxxxxxxxxxxx with "unsubscribe" in the subject or the body.
> List archive and information: http://lists.trolltech.com/qt-interest/
>
>
--
[ signature omitted ]