Solved QToolBar::clear(), can't add widget anew ?
-
Hi !
I've been struggling with this issue for a while : I can't add a widget to a QToolBar if it has been cleared beforehand.
I'm giving more information : I'm currently developing a basic drawing piece of software, and I want it to display a toolbar which enables you to modify some parameters of the tool you've clicked on (size of the brush, type of brush, color...).
Therefore, I would like to update the content of my toolbar each time a new tool is chosen.Each time I click on a button, a custom function is called to change the chosen tool depending on the sender (until that point, everything's fine).
Here I need to modify my QToolBar in order that only the parameters related to the chosen tool are displayed. So, in first place I call ui->mainToolBar->clear() and then a series of ui->mainToolBar->addWidget(myWidget).
For exemple, if I click on the paintbrush, I will see it parameters, I then click on the rubber, I will see its parameters, YET, if I click again on the paintbrush, its parameters won't reappear, the toolBar will stay empty.I've run several tests aside, and it clearly appears that a widget that has been added to a QToolBar can't be added anew if it has already been cleared once from it.
Am I right? Or have I forgotten to do something to make it work?
Is there a way to get round this problem?
I can't figure out how to solve it...Thanks in advance !
-
Hi,
The tool bar will take ownership of the widget so
yes, it will be deleted on clearthey should be deleted on destruction. However, why not just set your widgets invisible rather than deleting them ?[edit: fixed wrong assumption SGaist]
-
Thanks for your answer.
That's what I first thought reading the documentation, but what made me doubt is the fact that after the clear() instruction, if I write a qDebug()<<myWidget.text(), the right text of the widget still appears in the debug stream, which means that the object still exists, doesn't it ? If it exists and if I still have access to it, why can't I use it again? There is still something I don't really understand.
I've already seen the solution of widget's visibility on the internet, but it forces me to handle the widgets with their indexes by hand, which, when the number of parameters per tool will increase, will be a nightmare. I've always thought that handling objects in list with their indexes (assuming that we know them) for direct acces is an error-prone approach (except in sorting loops and similar actions, of course).
I could also code a loop checking all actions and decide if this action should be visible or not, but this will result in a code with a ton of if statements and it will then be hard to read and correct.So as I do not "like" this index handling approach (at first glance, but maybe there is a good way to handle item's visibility), I wanted to be absolutely sure that the clear()/addWidget() approach is a dead-end (which is apparently the case).
-
There's something kinda surprising going on here. I did a little test myself:
QPointer<QPushButton> btn_pointer(new QPushButton("Foo")); QPointer<QAction> act_pointer(mainToolBar->addWidget(btn_pointer)); qDebug() << act_pointer.isNull(); qDebug() << btn_pointer.isNull(); qDebug() << act_pointer->parent(); qDebug() << btn_pointer->parent(); mainToolBar->clear(); qDebug() << act_pointer.isNull(); qDebug() << btn_pointer.isNull(); qDebug() << act_pointer->parent(); qDebug() << btn_pointer->parent(); delete mainToolBar; qDebug() << act_pointer.isNull(); qDebug() << btn_pointer.isNull();
The output is:
false false QToolBar(0x12810dc0, name = "mainToolBar") QToolBar(0x12810dc0, name = "mainToolBar") false false QToolBar(0x12810dc0, name = "mainToolBar") QObject(0x0) true true
which means that
clear()
does not delete neither the action nor the widget. It seems to remove parent from the widget but it still tracks it in some other way because it still destroys it along with the action.
I don't know the rationale behind it. I suspect it's to allow the action to be added to other places and not have a surprise when we clear one toolbar and the action disappears from other places, but still it's kinda surprising. It could really use a better documentation at least.As for the problem - to add that widget back you should use its action instead i.e.
QAction* action = mainToolBar->addWidget(someWidget); mainToolBar->clear(); //removes the action but does not delete it mainToolBar->addAction(action); //so you can bring it back later
Personally I would go for hiding/showing the actions instead of clear/add.
I could also code a loop checking all actions and decide if this action should be visible or not, but this will result in a code with a ton of if statements and it will then be hard to read and correct.
Not necessarily. How about something like this:
QAction* action_foo = mainToolBar->addWidget(...); QAction* action_bar = mainToolBar->addWidget(...); QAction* action_bazz = mainToolBar->addWidget(...); ... QHash<int, QVector<QAction*>> tool_actions; tool_actions[some_tool] = { action_foo, action_bar }; tool_actions[some_other_tool] = { action_bazz }; ...
Then, whenever you select a tool you have a nice lookup table which you can use to show/hide the actions (important to show/hide actions, not the widgets!). No explicit indexes needed and no if statements.
-
Thank you very much for taking time to run tests ! It helped me to have a better understanding of how it works.
The idea of the hash-table seems great ! I'm going to follow this way.
Thanks for the help !
-
Wouldn't it be possible to just create a set of toolbars, one per tool, and then just hide/show the entire toolbar for the selected tool? It seems that would be easier than trying to hide/show individual controls on a single toolbar and worrying about the widget management involved. I'm rather new to Qt so I'm asking this as a question more than providing it as a suggestion.
-
This approach would use more memory while bringing no benefits at all (except maybe a little time for the developper). In general, it is not a good idea to instanciate a class multiple times if you don't really need to.
Here I don't need more than one toolbar as one is enough to do the job perfectly.
Moreover, I don't want to have a dozen of objects(toolbar) which do exactly the same thing (receiving and displaying the same type of objects at the same location on the screen). They would even share some actions (for exemple, the size of the brush would be used without modification in many toolbars).The solution of the hash-table is not that complicated, and it's better to spend time to learn more rather than to keep habits that result in heavy and sometimes "spaghetti" codes.
To make a comparison, if you want to connect many computers together, it's better to learn how to use a hub rather than to try to connect directly each computer to every others.
-
My apologies, I had not read your original post clearly enough to see that many of the toolbar widgets/actions would be used for all tools while only some needed to change. I was assuming a mutually exclusive set of widgets/actions for each tool. Certainly, if you only wish to use a single toolbar and only change some of the item, then what you propose makes sense.
I have a similar application with a set of tools for the user to select from, but in my case there are no shared widgets between the tools. I also need to modify the toolbar behavior for some tools to use an installed eventFilter that doesn't apply to all toolbars. Plus, I have a legacy user base where beginning users require the option to see a toolbar for a particular tool even if it isn't the currently selected one, and expect clicking an action on a toolbar that isn't for the active tool will activate that tool, so I'll have more than one toolbar visible at a time. Advanced users will turn off toolbars that aren't for the current tool to regain screen real-estate. So for me, keeping around individual toolbars is a necessity and though it does consume extra resources, it also improves performance as a toolbar can just be show/hidden and not rebuilt every time. But as I said, my situation certainly does not match your situation.
-
Exactly, as you need to display several toolbars at the same time and as they have different behaviours, you can't have just one instance. So the multiple instanciation is not a waste, but a necessity.
My case is way more basic, one toolbar is all I need :). -
After a check in the implementation, only the action is concerned by clear. Clear will simply call removeAction on all action it contains.
-
@SGaist I skimmed through it a little bit too, but aside from removing the action somewhere in there the parent of the widget gets removed also. Haven't figured out what exactly does that and what for, but I guess I have to work on my focus span :)
-
In fact, it removes the action from the toolbar itself. It calls QWidget::removeAction.
I also haven't yet found how the parenting is modified.