PDA

View Full Version : QComboBox - Few virtual methods



gruszczy
6th July 2008, 04:10
Hello everyone!

In my recent project I needed to inherit from QComboBox and add a new feature. Unfortunately, only two methods were virtual and could be overriden (showPopup and hidePopup). However, what I need is to add some functionality exactly at the moment, when index is changed. This could not be done.

Eventually I intercepted activated/currentIndexChanged signals, but that's really not the solution I want to use. I must guarantee, that this new QComboBox will do some thing before other objects intercept such signal. Therefore I had to add other signals, which doesn't satisfy me since it's not consistent with previous QComboBox.

Therefore I would like to ask, if anyone knows what is the reason for keeping so few methods virtual and if there is anyone who tried to create subclassed QComboBox and how he had done it?

jpn
6th July 2008, 18:17
Therefore I would like to ask, if anyone knows what is the reason for keeping so few methods virtual
Probably they didn't (fore)see any reason to add such virtual methods. You can send a suggestion (http://trolltech.com/developer/task-tracker) to add them.


and if there is anyone who tried to create subclassed QComboBox and how he had done it?
There is QxtCheckComboBox (http://docs.libqxt.org/classQxtCheckComboBox.html) but it's no really interested of current index changes...

gruszczy
6th July 2008, 23:29
Probably they didn't (fore)see any reason to add such virtual methods. You can send a suggestion to add them.

So I did.

I am not trying to criticise, especially that I am not too much into GUI stuff, just wondered if there is some specific reason for not keeping most (all) of the methods virtual - or at least those, that are public. It's always good to know something more.

I have managed to solve my problem, but I had to add new signals, which makes my combo box inconsistent with the old one - I don't like it really. But maybe something changes in the future version of qt and I'll be able to change it.

aamer4yu
7th July 2008, 06:45
What were u trying to achieve by the way ??
If u are looking to catch changes in the combobox, u can install filter on the combobox, and make ur own function to intercept it.

If thats not sufficient, you may have a look at -
QComboBox::setModel

QComboBox::setView

You may also have a look at QAbstractItemModel signals and protected functions. I dont know the detail implementation but I guess those might help you :)

caduel
7th July 2008, 08:08
Another possibility (for a subclass) to catch its own signals.

(Of course that does not always help. Sometimes the lack of virtual function or the fact that Qt extensively (and for good reason) uses the pimpl idiom can not effectively worked around, forcing you to reimplement parts of a class. Which you can, by copying its source. But which is to be avoided for maintenance reasons...)

HTH

gruszczy
7th July 2008, 10:47
Another possibility (for a subclass) to catch its own signals.

This is how I solved this. But since I have to add new signals for other objects to intercept, then I don't really like it.


What were u trying to achieve by the way ??
If u are looking to catch changes in the combobox, u can install filter on the combobox, and make ur own function to intercept it.

I would like to add history of user's choices to the combo box. Every time user chooses something from the combo box it stores information about what it is. Right now it catches 'activated(int)', changes history and then emits new signal. I have to take a look at these filters, maybe they help. Thanks.

aamer4yu
7th July 2008, 11:29
I guess QComboBox::setCompleter might be useful in your case :)

gruszczy
7th July 2008, 11:49
I guess QComboBox::setCompleter might be useful in your case

Completion? Why should I change or disable it?

aamer4yu
7th July 2008, 12:22
Well you can use the completer to read from your history.
This way you can help the user choose from the history. And when a user has finaaly entered/ selected one, you can add it in the combobox.

Am not clear what you are trying to do, and how u are doing it. But I thought if you are providing history to the user, completer might help. May be i misunderstood your problem :o

gruszczy
7th July 2008, 14:12
Nope, my box must provide API for reading history of choices, user doesn't need to get it. I'll show, how it looks now (it's Python + PyQt though):



class QComboBoxExt(QComboBox):

"""Extended combo box, that holds information about the history
of the user's choices."""

def __init__(self, parent = None):
QComboBox.__init__(self, parent)
self.__history = deque()
self.__current = 0
self.connect(self, SIGNAL('currentIndexChanged(int)'), self.signal)

def signal(self, index):
self.__history.appendleft(self.__current)
self.__current = index
self.emit(SIGNAL('changed'), index)

def getPrevious(self):
return self.__history[0]

def undo(self):
self.__current = self.__history.popleft()
self.setCurrentIndex(self.__current)

wysota
7th July 2008, 15:11
Adding new virtual methods is out of the question - it would break Qt's ABI. Adding a pseudo-virtual method with use of slots is possible though,

As for making or not making methods virtual - making a method virtual makes it possible to reimplement such a method and this is not always desired. One could ask why there is a "final" keyword in Java - because of the same reason...

As for the main problem - why do you need to catch activated() before anything else? You can save the history in a model and instead of connecting the external objects to the combobox connect them to the model.

gruszczy
8th July 2008, 01:58
As for making or not making methods virtual - making a method virtual makes it possible to reimplement such a method and this is not always desired. One could ask why there is a "final" keyword in Java - because of the same reason...

You actually don't answer any question ;-) Why it is not always desired? I assume the only reason is that QComboBox is not a mature class, that can change a lot during further development and user's that subclass it and override certain methods will get their classes broken. But then, all those methods are public, so there is really no danger. By making them public qt guarantees, that they won't disappear. And I don't believe that something as basic as QComboBox can be not a mature class.


As for the main problem - why do you need to catch activated() before anything else? You can save the history in a model and instead of connecting the external objects to the combobox connect them to the model.

I need to catch this activated, because external object (actually a widget holding a few combo boxes that share content) must intercept activated(int) (or rather in my class changed) and have the whole history properly set. If it catches the signal before combo box the history won't be right and it won't work.

Is there any way to catch intercept activated only by the combo box and then emiting signal that will be intercepted by everyone else except for the combo box?

wysota
8th July 2008, 02:21
You actually don't answer any question ;-) Why it is not always desired?
Because if I create some class and some method within it then I'm entitled, as the creator, to decide how the class is going to be used or not used. Your question is similar to another question - "Why make some methods or members of a class private?". If you answer one of the questions, you'll have an answer to the other one as well.


I assume the only reason is that QComboBox is not a mature class,
It's one of the oldest classes in Qt, so I guess one can say it is mature.


But then, all those methods are public, so there is really no danger.
This is not true. Consider the following class:

class X {
public:
X(){ value = 0; }
uint getValue() const { return value; }
void increaseByOne(){ value = value + 1; }
void doSomething(){ increaseByOne(); assert(value>0); ... }
private:
uint value;
};

Now imagine "increaseByOne" is made virtual and someone reimplements it as "value = value - 1". If you decrease a unsigned int by 1 when it is 0, you'll get a *HUGE* value which will be different from what the author had in mind. Now if you call doSomething(), the assert might be triggered and your whole app would crash. All that because someone reimplemented increaseByOne().... Now imagine the class is part of a dynamically loaded plugin interface over which you have no control.


By making them public qt guarantees, that they won't disappear.
It's not a matter of them disappearing, but rather doing now what they were intended for. And if you made all methods virtual (let's skip the issue of not working for a while), you'd get huge vtables for classes which would result in bloating the libraries and RAM with lots of completely useless data (as most methods would not be reimplemented) and additional overhead of table look-ups during runtime. This is not java, we do care about resources ;)


I need to catch this activated, because external object (actually a widget holding a few combo boxes that share content) must intercept activated(int) (or rather in my class changed) and have the whole history properly set. If it catches the signal before combo box the history won't be right and it won't work.
Ok, but is the goal to "catch the activated signal" or to "have the whole history properly set". Because if the latter, then maybe you don't need to catch the signal at all as already suggested.


Is there any way to catch intercept activated only by the combo box and then emiting signal that will be intercepted by everyone else except for the combo box?

I'm not sure if I understand the question, but if I do, the answer is "yes", but I won't tell you how, because I want to prevent you from shooting yourself in your leg or loosing all your hair. I really suggest you try to find a more proper design. Some controller at least... If you don't want it to be the model which is already in the combobox regardless if you want it or not, then make it a completely custom class of yours.

aamer4yu
8th July 2008, 06:01
I need to catch this activated, because external object (actually a widget holding a few combo boxes that share content) must intercept activated(int) (or rather in my class changed) and have the whole history properly set. If it catches the signal before combo box the history won't be right and it won't work.

Well, have u tried installing event filter ? You can very well capture keystrokes in the combobox. When 'Enter' key is pressed, do your own stuff and then call the base class implementation.
Wont this work for u ? You will be processing the info before the keystroke is sent to someone else and before the activate signal ?? Am I wrong ?

gruszczy
15th July 2008, 18:06
@wysota: This discussion is becoming philosophical, instead of technical. And yeah, I believe all methods should be virtual and all members should be public. That's why I am using Python :-)

@aamer4yu: I'll try this event filter, but I have bad feelings about this ;-)

wysota
15th July 2008, 19:13
@wysota: This discussion is becoming philosophical, instead of technical.
You started it. You asked for a reason - I gave you a reason.


And yeah, I believe all methods should be virtual and all members should be public. That's why I am using Python :-)

Funny that you mention it, as Python has the concept of private class components as well.

gruszczy
16th July 2008, 15:16
You started it. You asked for a reason - I gave you a reason.

Technical reason would be much better :-) I have taken a look at combo box in wxWidgets - most public methods there are virtual.


Funny that you mention it, as Python has the concept of private class components as well.

AFAIK only name mangling - and even then one can reach those members outside the class. It is not used as a mean of hiding data from the user, but to prevent name clashes when it is inherited. When you want to make something private, you just say that it is private. If one uses it then, then it's his fault if his software gets broken.

wysota
16th July 2008, 17:08
You can do the same in C++. The check is made purely on compiler level and you can cheat it by using casting.