Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct
How can I receive signals emitted by setupUI(), to capture the initial state of a widget configured in Qt Designer?
I have created a 'SettingsWidget' in which contains some basic controls (checkboxes and spinboxes) and have set the initial values appropriately in Qt Designer. In code I create an instance of SettingsWidget, call setupUI() to create its components, and then hook-up the various xxxChanged() signals to the slots of the object I want to control.
It works as expected, but unfortunately I do not get a chance to receive the signals when the components are set to their initial state by setupUI(). This is because I setup the connections after setupUI(), and obviously I can't set them up first because the components haven't been created yet.
That is, setupUI() is an 'atomic' operation which both creates the component widgets and sets their initial state. Ideally this would be split in two so that I could call e.g. 'createUI()', then setup my connections, then call 'setupUI()' and receive the signals as setup occurs. Unfortunately this is not how Qt Designer seems to work.
Of course, once setupUI() is complete I can write code to read back the state which has been set, but this is extra coupling and feels somewhat hacky. How do people usually handle this setup phase?
Thanks in advance!
Note: For completeness, my current solution is to have a 'simulateChanges()' method on my SettingsWidget which I call after I have set the connections up, and which manually emits valueChanged() signals for each component. It feels ugly though and requires me to keep this up-to-date as I add or remove component widgets.
Note 2: This question was originally posted on StackOverflow
@David-Williams You can connect signals to slots in designer, did you try to do so?
Thanks for the reply, but in this case the object being controlled by the SettingsWidget is not a GUI component and so does not exist in Designer. It is just an object which does some calculations based on the settings which are provided. Hence it is created in code, and I miss the opportunity to receive the initial signals which are emitted as the SettingsWidget is created by setupUI().
I feel a nicer solution would be if Designer actually generated three functions. 'createUI()' could create the components and 'initUI()' would initialise them, and setupUI() would then just call these two function. It would be backwards compatible, and in my case I could skip setupUI() and inject my connection code between my own calls to createUI() and initUI().
But maybe I am still missing a trick somewhere?
What is usually done is that you create your non-GUI objects, your GUI-objects, connect everything needed and then have a method that loads whatever data is needed and thus will trigger the signals and slots to set everything up. The last part is usually done using a single shot QTimer with a delay of 0 meaning that it will fire the next time the event loop runs.
David Williams last edited by David Williams
Thanks for the reply. After everything is created and connected I could indeed set the controls to the desired initial value through my code, and this will cause the signals to be emitted and the settings to be propagated. However, there are then two places where the initial state is defined - firstly in the Designer property browser and later through my own code (which then overrides whatever was set in Designer). This will be confusing if someone else tries to modify the code base. Furthermore, Designer will validate the initial state at design time (I think...) to ensure that a given component value is between its Designer-specified min and max. This is nicer than the run-time validation which would happen if I set up from code.
My current approach is in some ways a variation of what you propose - after setup I have code which simply re-emits the signals which were previously emitted (and missed) from setupUI(). But this requires some maintenance each time I add/rename a control, and it would just be nicer to receive the signals the first time.
Are you connecting both classes externally ?
@SGaist One of the classes (SettingsWidget) is a GUI component but the other class is not, therefore I have no choice but to connect them via my own code outside of Designer. I think this is the crux of the problem - by the time the Designer-generated setupUI() has finished executing the various onChanged() signals from setting the initial values have already been fired and so my connections are set up too late. I can get around it by re-firing the signals manually or setting the values again from code but it just feels a little ugly.
Perhaps I could alternatively create a proxy widget to expose my non-GUI class to designer, but again it doesn't fee like a very clean solution. If Designer could split 'setupUI()' into 'createUI' and 'initUI()' then I could inject my calls to connect() in between them, but this isn't how it currently works.
@David-Williams You can still connect to slots in your GUI in Designer (not directly to your settings object). And then use your settings object in those slots.
@jsulm Thanks, I think I might be able to work with this, or at least you have given me some inspiration. I can add some slots to my higher-level GUI (perhaps my MainWindow) and then connect the SettingsWidget's onChanged() signals to those in Designer. The new MainWindow slots could then take care of forwarding the changes to the non-GUI object which should be controlled. At least it seems a bit cleaner than my current solution.
I think I'v actually made my life a bit difficult by splitting my application into too many UI files and/or classes. For example, my SettingsWidget (just a collection of spin boxes) is in a seperate UI file from my MainWindow. This makes it quite difficult to connect signals/slots between them as I don't have a 'proper' Designer plugin for the settings widget (just using the 'promote widget' feature to insert one). I think I might revisit some design decisions here.
to answer to your original post, I usually have have a
void setDefaultLayout()function that fills the ui, created by code or by QtDesigner, with values.
That way you also have the possibility to reset your ui without restarting.
In your special case of a Settings Widget, I usually combine such a widget with QSettings to save values/states. And that basicaly demands a function to fill the ui with the stored values.
Thanks, I think that in summary I will try to restructure my code to capture the initial state through slots in the MainWindow (as described previously), and I will also implement loading of settings via QSettings (at which point the Designer-set values won't mater much anyway).
I think I've probably got enough to come up with a solution now, so I'll mark this question as answered. Thanks to all for your input!