What approach do you take for dialogs where an action is to be performed?
-
@sierdzio
For the purposes of the question here, I see that as a variant of #2.When does your dialog close? Immediately after sending to the controller, unconditionally? The dialog requests a result from the controller? The controller sends a signal back to close the dialog?
-
@JonB said in What approach do you take for dialogs where an action is to be performed?:
When does your dialog close? Immediately after sending to the controller, unconditionally? The dialog requests a result from the controller? The controller sends a signal back to close the dialog?
Dialog closes when user clicks a button (OK, Close, Escape etc.). Handler for that button emits the relevant signal (accepted() or rejected()) with relevant data.
-
@sierdzio
So in that case, if you close the dialog unconditionally on OK and then the "back-end"/controller/whatever reports an error during the action (I didn't like the file path you picked, or whatever), the dialog has gone and the user cannot correct an error. That is often not nice. This is why the decisions are so unclear to me.... -
-
A standalone dialog/window, named A, has a button on it to create a backup. It has the logic to do the backup, or that's elsewhere.
-
Now I decide that actually I want to offer the user a couple of options to affect the backup ("Do you want to allow overwrite?", "What things to exclude?"). So I create a (modal) dialog, named B, with those couple of parameters. They will be passed to the backup routine.
-
That gives two simple choices about how to implement:
a. Move the code for backing up into dialog B. That dialog now does the backup when the user clicks OK or Proceed, and can access the user's choices easily to produce the parameters. The backup is happening from dialog B's OK button.
b. Keep the backup happening in A. When user clicks OK in modal dialog B, it is dismissed but does nothing but
accept()
. A can still ask B for the parameters the user filled in (though you have to write functions to access them) while it constructs the parameters to make the backup. The backup is happening back in A, after dialog B has been dismissed. -
-
@kshegunov
Very briefly, I don't feel that answers the question I am trying to get at, as per my further questions to @sierdzio.Moving the action code into a controller does not obviate the decision as to who is responsible for invoking the controller to perform the action. Is that to be the OK button on the dialog which gathers further parameters, or is that to be in the caller of the dialog?
There are two basic patterns possible with a modal dialog asking the user for some information required for an action:
-
The dialog itself gathers the information and calls for the action to be performed with those parameters when OK is clicked, in some shape or form. (The dialog is still open in this scenario.)
-
The dialog closes on-screen (but not in memory) and returns
accept()
to the caller when OK is clicked. The caller uses methods in the dialog to retrieve the parameters and the caller then invokes the action with those parameters, in some shape or form. (The dialog is closed this scenario.)
I fret between which of the two approaches to use when.
-
-
I vote for the first option, then. Dialog sends a signal and waits for a reply or calls controller directly. It is closed when controller decides it can be closed.
This is a more future-proof solution, I think, but it is a pain in the ass to implement.
-
@JonB said in What approach do you take for dialogs where an action is to be performed?:
The dialog itself gathers the information and calls for the action to be performed with those parameters when OK is clicked, in some shape or form. (The dialog is still open in this scenario.)
That'd be the/my preferred and (the) flexible way to do it. Having a controller object however makes it a bit easier as both dialogs could be created, initialized and controlled from it, and the data you're working with kept in said object. This also lifts the need to actually derive from
QDialog
(twice). -
@sierdzio, @kshegunov
OK, we are now in business for the brainstorm! (If possible, can we ignore having a controller for the current purposes.)You are both going for the "dialog initiates action rather than returning to caller to do so". Now consider the following.
Case #1:
I have an action, let's say "do a backup" or "delete some files", but it really doesn't matter. When the user clicks a button to perform it, I want the code to check with the user that they are "sure".
For this I almost certainly write an "Are you sure? Yes/No" dialog. It returns
accept()
orreject()
to the caller. The caller still performs the action. This is the usual approach taken in, say, either the native or Qt level of putting up a "message box" for confirmation. Now, the OS or Qt function could instead have taken the approach of saying that the caller specifies a "callback function" (the backup routine) which the confirmation dialog invokes if the uses clicks Yes/OK/Proceed, but that tends not to be what is provided in this case.Case #2:
Now I want to add to that confirmation dialog a checkbox parameter, like "Continue on error" or what-not. (Let's say it's my own dialog, not a system message box where I cannot do that.)
- Either: I have to change code so that now the dialog invokes the action so that it can pass that parameter on to it;
- Or: I allow it to still return
accept()
to the caller, the caller then has to query the (closed-but-still-in-memory) dialog to get the value of the parameter so that the caller can pass it on.
Case #3:
Finally, I now decide to put 10 extra "widget-parmeters" onto the dialog, to ask all sorts of things for the action. At this point it's such a pain to have to be able to pass all those parameters back to the caller that I probably do change the dialog to invoke the action with all these appropriate arguments itself instead of just returning
accept()
.It's the middle case #2 that I fret over. There are are just one or maybe two widget-parameters on the dialog. I only have to provide one (or maybe two) method in the dialog to retrieve the extra parameter. I feel I'm between a rock and a hard place deciding which of the two approaches feels "best"....?
-
Forgetting for a second the controllers, you'd want self-contained units (i.e. dialogs) and that applies to case #2 as well. What is "proper" is to have the dialog keep internally the kind of data the user has selected, be it checkboxes, text or w/e and then after the acceptance pull that from the outside. It doesn't really matter if the outside is another dialog or a widget, you'd still execute it the same way. This also means the validations and such should be done in the UI component itself, which can be really finicky as it may require injecting additional data from outside. The real problem is however that this requires a lot of boilerplate code (as you've already probably experienced), so it's somewhat exhausting to do for all dialogs and in all cases.
That's where the "controller" object shines, as you can manage multiple dialogs (forms/ui elements) all together as a single unit, so you don't in fact need to transfer data between the different UI components - all is tidily collected in the controller; and it's the controller that initializes the UI so it puts the data directly to the UI, and does the validation.
-
@kshegunov
Yes, but I will just say. Had I been paid a penny for my work, or had I written the code myself, I should be more disposed. As it is, I have inherited 50+ dialogs, no comments, and no time to rewrite anything. To abstract the data from the dialogs requires an awful lot of non-GUI classes to be written to represent the data. And given that Python has no type-checking, it's not a nice exercise....! -
@JonB said in What approach do you take for dialogs where an action is to be performed?:
Had I been paid a penny for my work
If you work for free, I have way too much on my plate ... :)
To abstract the data from the dialogs requires an awful lot of non-GUI classes to be written to represent the data. And given that Python has no type-checking, it's not a nice exercise....!
Perhaps, however that's how it should be done in the long run. If you only care about immediately getting it to work, then it doesn't really matter the approach you take, because it's going to end as patch, over patch, over patchwork. In the end it's going to be spaghetti all 'round, but that's how it is with no-planing-no-time-to-code-properly projects.
-
@kshegunov said in What approach do you take for dialogs where an action is to be performed?:
Perhaps, however that's how it should be done in the long run. If you only care about immediately getting it to work, then it doesn't really matter the approach you take, because it's going to end as patch, over patch, over patchwork. In the end it's going to be spaghetti all 'round, but that's how it is with no-planing-no-time-to-code-properly projects.
tell me about it, I have a legacy project, I'm working on, in my free time. After the first rough cleanup, I ended up deleteing 10000 lines of code (not commets because there are none x) ) out of mainwindow.cpp, and it's still 15000 lines long...
Takes QtCreators completer up to 20 secs, when you type
ui->
[EDIT: Replies forked to https://forum.qt.io/topic/96617/usability-of-qt-creator-s-clang-code-model -- JKSH]
-
@JonB said in What approach do you take for dialogs where an action is to be performed?:
It's the middle case #2 that I fret over. There are are just one or maybe two widget-parameters on the dialog. I only have to provide one (or maybe two) method in the dialog to retrieve the extra parameter.
If it's only 2 parameters, just group them together with the button (e.g. inside a
QGroupBox
orQToolBox
). Get rid of the second dialog altogether (or make it "Are you sure?" only). Now you don't need to pass parameters, and you don't need to agonize over where to put the logic.@JonB said in What approach do you take for dialogs where an action is to be performed?:
This question is applicable to my Qt programming, but also to desktop UI programming in general even if I were not using Qt.
My code is full of (modal) dialogs. This is fine as far as I'm concerned; mobiles are for wimps. I don't want a discussion on whether that bit is good or bad.
Often a sub-dialog --- which will be invoked from a parent dialog or a window or whatever --- is needed to get a parameter or two from the user for the proposed action to be performed.
Even for non-mobile GUIs, I'd recommend keeping things simple and have as few dialogs as possible. Multi-level dialog chains make life harder than it has to be.
-
@JKSH said in What approach do you take for dialogs where an action is to be performed?:
If it's only 2 parameters, just group them together with the button (e.g. inside a QGroupBox or QToolBox). Get rid of the second dialog altogether
Since you've written, I'll reply. Unfortunately not. Invoker is a page with multiple buttons, each one leading to an action/dialog. There is no room/clarity to start having that offer sub-options etc.
-
@JonB said in What approach do you take for dialogs where an action is to be performed?:
Invoker is a page with multiple buttons, each one leading to an action/dialog. There is no room/clarity to start having that offer sub-options etc.
Something like
QToolBox
can help. It can show every available "action", yet only expose the buttons + parameters of one action at any given time time.