PDA

View Full Version : Cursor not visible



jpujolf
9th May 2007, 17:12
Hi all,

I'm working on a Programming Framework that hides GUI fighting to programmer, that only has to center his efforts into "Business Objects". As a part of the framework there is a forms hierarchy that controls user input.

I've an undesired behaviour on aQLineEdit. This is the scenario :

I've a form ( derived from QDialog ) containing some widgets ( QLineEdit, QComboBox, etc. ). I have all the control about tab order ( in few words, I'm ignoring tab order ). Every "FocusOut" Event, I calculate wich is the next widget to get the focus.

This is necessary, because every time I receive a FocusOut event, I perform data validation ( programmer's Business Objects should do the work ) and if validation is OK, I step to the next widget, else I retain user input to the last widget. Also Business Object can activate / deactivate widgets depending on the values introduced by the user.

The problem appears when the ValidateField method is called. Inside it, programmer may show error messages if data is invalid, showing a descriptive message to the user in two ways : with a messagebox or through the QStatusBar ( really, in a QLabel inserted with QStatusBar->addPermanentWidget ( ... ), to show an static red-colored message until input error is solved ).

If error messages are showed by a MessageBox, the focus returns to the last widget & a blinking cursor is shown. But when the mesage is shown through that StatusBar, the focus returns but no cursor appears !!

This is confusing to the final user... Any ideas ?

jpujolf
11th May 2007, 09:52
Nodoby has any clue about this strange behaviour ?

wysota
11th May 2007, 10:12
Why are you using such a strange way to validate? I suggest you use a QValidator subclass to validate the data and emit a signal from within validate() stating whether input is valid or not. If it's invalid, you can set the focus back to that widget owning the validator and still use the standard tab order feature.

jpujolf
11th May 2007, 10:35
Perhaps you misunderstood me or I didn't explain well. I use QValidator, I'va made some format-specific validators, ... to test input syntactic correctness, but when the user presses return / tab, the final value must be tested against a database, or some type of calculation must be done, or...

As I said, in my first post, I'm working in a core that hides GUI to the programmer, so in the other side the "Business Logic" only receives a "ValidateThisField" method call. If it answers OK, I make the step, if not I remain inside the field.

QValidators call a fixup method, but doesn't allow to stop the process if that "final validation" fails. So I decided to catch the focusOut event to put here an extra validation.

Is that an error ? I think this isn't I do need validate on every FIELD EXIT. And this validation could be time-comsuming ( query launch to database, test, ... ).

So anyway the problem is not the method to validate. I've been testing and if I show a message directly in a QStatusBar ( showMessage () ) between a focus out and a focusin event to the same widget, the cursor dissapears in that widget

And that's VERY strange, and my real problem...

And regarding to standard tab order : I do have to override it because Business Logic could, while working ) activate / deactivate the edition of some fields. And if I use standard tab order, you know what happens with tab order when you disable / enable widgets ? It's lost...

wysota
11th May 2007, 11:11
Perhaps you misunderstood me or I didn't explain well. I use QValidator, I'va made some format-specific validators, ... to test input syntactic correctness, but when the user presses return / tab, the final value must be tested against a database, or some type of calculation must be done, or...
So can't the validator do it?


As I said, in my first post, I'm working in a core that hides GUI to the programmer, so in the other side the "Business Logic" only receives a "ValidateThisField" method call. If it answers OK, I make the step, if not I remain inside the field.
I don't really see a problem... Just make the call from the validator instead of the widget.


QValidators call a fixup method, but doesn't allow to stop the process if that "final validation" fails. So I decided to catch the focusOut event to put here an extra validation.
Emit a signal from within the validator stating if the input is valid or not. If it's not, place the focus back in the lineedit owning the validator sending the signal.


Is that an error ? I think this isn't I do need validate on every FIELD EXIT. And this validation could be time-comsuming ( query launch to database, test, ... ).
It's a "strange approach" and it doesn't work :)


So anyway the problem is not the method to validate. I've been testing and if I show a message directly in a QStatusBar ( showMessage () ) between a focus out and a focusin event to the same widget, the cursor dissapears in that widget
IMO the problem is that the status bar might be stealing the focus, so it's better not to hack the focus but just set it back. And don't do the validation on focus loss - you only need to validate when something actually changes and that's what QValidator is responsible for.


And regarding to standard tab order : I do have to override it because Business Logic could, while working ) activate / deactivate the edition of some fields. And if I use standard tab order, you know what happens with tab order when you disable / enable widgets ? It's lost...
You can make a line edit read only instead of disabling it, AFAIR. And you can always set the tab order again, so losing the order is not that much of a problem.

jpujolf
11th May 2007, 12:04
So can't the validator do it?

I don't really see a problem... Just make the call from the validator instead of the widget.


Validator Can, but musn't, because validation may be VERY time consuming !!! i.e ( I'm in Spain, so dates will be like DD/MM/YY or DD/MM/YYYY. See this code, is my "smart date validator" ( limits years to 19xx, 2xxxx, accepts two formats DD/MM/YY or DD/MM/YYYY and accepts "smart input" )



MDateValidator::MDateValidator ( QObject * parent ) : QRegExpValidator ( QRegExp ( "[0-9]{1,2}[/\\.]{0,1}[0-9]{1,2}[/\\.]{0,1}[0-9]{2,4}" ), parent ) {}

QValidator::State MDateValidator::validate ( QString & input, int & pos ) const
{
// Empty date => We accept it
if ( input.length() == 0 ) return Acceptable;

// Must follw minimal specifications...
if ( QRegExpValidator::validate ( input, pos ) == Invalid )
return Invalid;

// 0 : We allow ".", but change it by "/"
input.replace ( '.', "/" );

// PreparacionInput prepare
// 1 : day must be 1..31. If first digit is between 4..9 => we change it to 04/09
if ( input.left ( 1 ) > "3" )
{
input = "0" + input;
pos++;
}

// If they introduce one-digit day and a bar we add the leading "0"
if ( ( input.length() == 2 ) && ( input.mid ( 1, 1 ) == "/" ) )
{
input.insert ( 0, "0" );
pos++;
}

// We do know the day, so we can make a first validation
int Dia = input.mid ( 0, 2 ).toInt();
if ( Dia > 31 ) return Invalid;
if ( ( Dia == 0 ) && ( input.length() < 2 ) ) return Intermediate;

// Append bar if they press the first month's day without pushing bar...
if ( ( input.length() > 2 ) && ( input.mid ( 2, 1 ) != "/" ) )
{
input.insert ( 2, "/" );
pos++;
}

// If there is no month, they are OK, by now...
if ( input.length() < 4 ) return Intermediate;

// If they enter a month before October without the leading zero, we put it
if ( ( input.length() == 5 ) && ( input.mid ( 4, 1 ) == "/" ) )
{
input.insert ( 3, "0" );
pos++;
}

// 2 : Month between 1 and 12, but considering the day...
int Mes = input.mid ( 3, 2 ).toInt();
if ( Mes > 12 ) return Invalid;
switch ( Mes )
{
// 30 day months
case 4 : // April
case 5 : // June
case 9 : // September
case 11 : // November
if ( Dia > 30 ) return Invalid;
break;
// Special case : February, we don't know the year, so 29 is possible...
case 2 :
if ( Dia > 29 ) return Invalid;
break;
default :
break;
}

// 3 : a month beginning by 2..9, We have to insert a leading 0
if ( input.mid ( 3, 1 ).toInt() > 1 )
{
input.insert ( 3, "0" );
pos++;
}

// We append month / year separation, if the put first year's digit without the bar
if ( ( input.length() > 5 ) && ( input.mid ( 5, 1 ) != "/" ) )
{
input.insert ( 5, "/" );
pos++;
}

// 4 : Year could begin by 1 ( 19xx ) or 2 ( 2xxx )
if ( input.mid ( 6, 1 ).toInt() > 2 )
{
input.insert ( 6, "20" );
pos += 2;
}

// Two / four last digits are for the year...
int Anyo = input.mid ( 6, 4 ).toInt();
if ( Anyo < 1900 ) return Intermediate;

if ( MDate::IsValid ( Anyo, Mes, Dia ) )
{
// Here must be your suggested call / signal
return Acceptable;
}
else
return Invalid;
}


While user enters the date, this validate method tests & corrects user input. But when he presses ENTER or TAB key, even if the date is a valid one, Business Logic may decide it's not a valid date ( i.e a day before today for a payment )

And here becomes the problem. I cannot assume a call to Business Logic EVERY time a date is Acceptable, because that call could be very time-expensive.



It's a "strange approach" and it doesn't work :)


OK, I accept perhaps is an "strange approach", but is working. And it's quick and efficient. Sometimes we must break "programming-guidelines" to obtain a better performance...

And I repeat that the problem is not in the validation style but in the fact that everything is working OK, but the cursor dissapears when between the focusout and the focusin I show a message on an external StatusBar. But when i show a modal message, all is running OK :confused:



Emit a signal from within the validator stating if the input is valid or not. If it's not, place the focus back in the lineedit owning the validator sending the signal.

...

IMO the problem is that the status bar might be stealing the focus, so it's better not to hack the focus but just set it back. And don't do the validation on focus loss - you only need to validate when something actually changes and that's what QValidator is responsible for.


But you will agree with me that I will have the same problem. When I emit the signal, I receive a "invalid" response and move the focus, "I'm fuc***" :o

I agree with you that StatusBar is stealing the focus, But I don't understand why...

I'm calling a method like this in the focusout event ( pseudo-code )



void MQEntryFormPrivate::FieldExit ( QWidget * Sender )
{
QWidget * pNext = Sender;

// If is valid, we step to the next widget, else we remain
if ( BusinessObject()->ValidateField ( Sender ) )
pNext = this->GetNextFocus ( Sender );

// Some irrelevant stuff here...

pNext->setFocus();


Inside business object's ValidateField code, user can show messages, activate & deactivate fields, launch queries, ....



You can make a line edit read only instead of disabling it, AFAIR. And you can always set the tab order again, so losing the order is not that much of a problem.


Yes, a QlineEdit. But not a QComboBox, or QRadioButton... And I do need to control that kind of widgets too :(

wysota
11th May 2007, 12:32
While user enters the date, this validate method tests & corrects user input. But when he presses ENTER or TAB key, even if the date is a valid one, Business Logic may decide it's not a valid date ( i.e a day before today for a payment )
Not really. You can fetch the "possible valid" state from the logic and cache it somehow and use an intermediate state when you suspect the input to be incomplete.


And here becomes the problem. I cannot assume a call to Business Logic EVERY time a date is Acceptable, because that call could be very time-expensive.

I think I just answered that as well :)



OK, I accept perhaps is an "strange approach", but is working. And it's quick and efficient. Sometimes we must break "programming-guidelines" to obtain a better performance...
What happens if you just keep pressing tab (possibly inside an invalid field)?


And I repeat that the problem is not in the validation style but in the fact that everything is working OK, but the cursor dissapears when between the focusout and the focusin I show a message on an external StatusBar. But when i show a modal message, all is running OK :confused:
As I said in the previous post, you're manipulating with the focus too much. Remember that focusOut causes a focusIn elsewhere and then you force the focus back which again causes a focusOut and focusIn. What does your focusOutEvent look like anyway?



But you will agree with me that I will have the same problem. When I emit the signal, I receive a "invalid" response and move the focus, "I'm fuc***" :o
Sure, but this is easily overcomable. Check if calling selectAll() on the line edit helps.


I agree with you that StatusBar is stealing the focus, But I don't understand why...

I'm calling a method like this in the focusout event ( pseudo-code )
I think you should just reject the focus out event.


Yes, a QlineEdit. But not a QComboBox, or QRadioButton... And I do need to control that kind of widgets too :(
Maybe you should just use a QDataWidgetMapper? Most of the things you require are already there, I think... Anyway, you can repair the tab order with a simple function, as I mentioned before.

jpujolf
11th May 2007, 13:08
As I said in the previous post, you're manipulating with the focus too much. Remember that focusOut causes a focusIn elsewhere and then you force the focus back which again causes a focusOut and focusIn. What does your focusOutEvent look like anyway?


Just calls my form's FieldExit method... :crying:



Sure, but this is easily overcomable. Check if calling selectAll() on the line edit helps.


:o I'm doing it... And it doesn't helps me...



I think you should just reject the focus out event.


You mean calling QEvent::ignore () ? Is the only test I've not done...



Maybe you should just use a QDataWidgetMapper? Most of the things you require are already there, I think...


QDataWidgetMapper could be a solution if I was using it against a model. Is not the case...

Anyway, thanks for wasting your time answering me. You helped me a lot !! :)

wysota
11th May 2007, 15:05
Just calls my form's FieldExit method... :crying:
I don't think that's enough.


:o I'm doing it... And it doesn't helps me...
Then probably the widget doesn't have focus after all...



You mean calling QEvent::ignore () ? Is the only test I've not done...
Yes.


QDataWidgetMapper could be a solution if I was using it against a model. Is not the case...
So it's only a matter of implementing a model :) It has all you need including the validation inside QAbstractItemModel::setData()


Anyway, thanks for wasting your time answering me. You helped me a lot !! :)

If I felt like wasting my time, I wouldn't be here.