PDA

View Full Version : How to deactivate all widgets except one on form startup or show?



Andrew K. Kabaghe
19th December 2020, 13:19
Dear everyone,
I have designed a QDialog form on which have placed different widgets to do some calculations.
However, everything is working according to plans.
Except that I want to disable all widgets except one for initial input.
I want widgets to follow tab order. After being done entering data, it should trigger or enable the next button to the end.
Previous widgets should loose focus and become disabled as soon as am done with them. But when I click the last button, the first widget should become active again, to allow fresh entry of data.
I used PyQt5 designer version 5.11 to design.

ChrisW67
20th December 2020, 05:31
Look into the possibility of installing an event filter (https://doc.qt.io/qt-5/eventsandfilters.html#event-filters) on every child widget. In the filter function check for QEvent::KeyPress, QEvent::FocusOut and QEvent::FocusIn and try to weave the magic here. The main problem I see here is that a disabled widget, i.e. the next one, cannot get the keyboard focus on Tab etc. You need to enable the subsequent widget before the focus can move there, and you need to do that in response to something happening in the present widget before Qt tries to move the focus. Normally Tab keystrokes are not presented to the widget, so you will have to filter for these as well.

If this is really a serial process you could consider using QWizard instead.

As a data entry form I would find the behaviour you seek potentially counter-productive. For example, the form assumes nobody ever mistypes (needs to go back and correct) and forces them to complete a process with bad input.

Andrew K. Kabaghe
20th December 2020, 08:37
Thanks for your valuable response.
The challenge I have is that in my application, there is:
1. A QLineEdit which I may alias here as "lineEditIncome" for inputting integers which is connectected to a QPushButton (pushButtonComfirmIncome) to confirm input.
2. Next is another QLineEdit (linEditAgentFee) for inputting integers, which has a pushButtonAgentFees for confirmation as well.
3. This is also followed up by Radio buttons, say: a, b, c, d for other expenses.
4. After which user has to click on two final pushbuttons for adding expenses (to two Line Edits) and calculating allowance (which is the difference between Income and all costs).

The problem is that, if user skips any steps (eg inputting income or Agent Fees), terminates the app or if he forgets to choose one of the above radio buttons, will end up with wrong output, or if he forgets to click "Add Expense" push button which populates a "total expense" Line Edit, the program crashes as too :(.

I know Event Handling can take care of that, but am not very familiar with making event handling and slotting it into the code.
Therefore, for the time being, I wanted to deactivate all buttons and make current action enable the next in put widget.

Maybe, in case of the wrong input as you correctly pointed out, the previous widgets shout remain active.
In Delphi Pascal, it was easy to do that onFormShow but with Python and Qt5,11, am like a baby who is learning how to walk :'(

ChrisW67
21st December 2020, 03:47
If I am understanding you correctly then there is a distinct Confirm button for each input in sequence.

In Designer, set only the first widgets as enabled and all other disabled (or do it in the constructor of your form class).

Each QPushButton instance will emit a clicked() signal when the user confirms the input on an associated input box (line edit, spinbox, whatever).
You connect that signal to a slot (in the form class). In that slot you can perform complex checks on the value and if it is good:

Call setEnabled(true) on the next logical widgets (editor and button)
Call setFocus() on the next logical editor widget
Call setEnabled(false) on the present editor and button widget

As you are using Designer, you can probably rely on the automatic connections for each QPushButton clicked() signal to the parent form widget onButton1Clicked() slot where it exists. ( I assume that the Python equivalent of uic does this)

There are other ways to handle this with a single slot using a QSignalMapper, but let's toddle before we try to walk or run.

Andrew K. Kabaghe
22nd December 2020, 19:03
Thank you very much though am still wondering how and which code I may use to disable all widgets on Form startup and leave only one widget active?
Hasn't the form got any options? I mean doesn't the form emit any signals we can manipulate to disable or enable widgets?

d_stranz
22nd December 2020, 22:16
I mean doesn't the form emit any signals we can manipulate to disable or enable widgets?

If you are using Qt Designer to create the form, then set all widgets except the first one as disabled in Designer.

I am not sure what kind of signals you would expect the form to emit. The form itself knows nothing really about the widgets it contains except for the tab order, so what can the form tell you? You have two ways to determine (yourself) what the user is doing in the form:

1 - Events, such as focusInEvent(), focusOutEvent(), enterEvent(), leaveEvent(), etc. which occur as the user moves or changes input focus from widget to widget. These are received at the individual widget level (ie. a QLineEdit), not the form, so the only way you can watch for them is to install an event filter on the editing widgets and monitor that filter in your form widget.

2 - Signals emitted by editing widgets when the user has finished editing, changed a selection, clicked a checkbox, etc. You can connect slots in your form widget.

There are some things you are going to find it very difficult to control. If you have all widgets except one disabled, then if the user clicks the Tab key, likely nothing will happen since there is no enabled widget to receive the focus. And generally, you can't intercept the Tab key except by using an event filter. And even then, you can't enable the next widget in that filter so the Tab goes to it because that enable event won't get processed until after the Tab event has finished (and found nowhere to go).

Like ChrisW67 says, you are implementing a very non-standard (and IMO very user unfriendly) UI. You would be better off using Qt's tools for validation (QValidator), completion (QCompleter), input masks, or tool tips to guide your users into making the correct inputs into your form.

As I said above, most editing widgets have signals that tell you when something has changed. If validators or other tools don't work for you, then use those signals to see what has changed and then enable / disable other widgets as appropriate.

Lesiok
23rd December 2020, 08:03
Or maybe use the QWizard class ?

d_stranz
23rd December 2020, 16:28
Or maybe use the QWizard class ?

That could get very tedious. In the early days of the Web, the US Postal Service implemented a system where you could register packages for shipment, pay the fees, and print the shipping label. All you had to do then was give the package to your postman when your mail was delivered.

I used it once. On the first form, you entered the first name of the person you were sending the package to, then clicked Next and waited for the next page to show up (remember, these were dial-up modem days). Then you entered the family name, and clicked Next. Then the first line of the address, Next, the second line of the address, Next, the city, Next... It took 15 minutes or more to enter the complete address, and if you messed up, you started over.

Linear thinking like that leads to a rigid and unfriendly UI and a horrible user experience. If the OP really needs to have fields entered and validated in a specific order, a QWizard might be a good idea but it could also lead to a nightmare like the Postal Service created.

I think there are enough tools available with validators, completers, and so forth that the OP can have control over the sequence. The problem, as ChrisW67 said, is what happens if you make a mistake and need to go back, but the UI has locked you out because it disabled that widget?

Lesiok
23rd December 2020, 17:05
Linear thinking like that leads to a rigid and unfriendly UI and a horrible user experience. If the OP really needs to have fields entered and validated in a specific order, a QWizard might be a good idea but it could also lead to a nightmare like the Postal Service created.With the same knife, you can slice the bread and cut your finger.