On screen keyboard


Recommended Posts

I'm having trouble getting the on screen keyboard to display. I would like it to appear when the user selects a text input box in a modal popup window.

How should this be done, I've tried setting the System.OnScreenKeyboard variable to 1, 2 and 3 without much luck.

Any help would be appreciated.

Many Thanks

Angus

Link to comment
Share on other sites

  • 4 years later...

I tried

System.OnScreenKeyboard=2
Private String Name = System.EntryDialog("enter name here")

 

but when I click on the on screen keyboard, the characters will not be input to the dialog

 

How to use the built in on screen key board with entrydialog?

 

thanks

Link to comment
Share on other sites

You need = 3 instead of =2.  There are 4 options:

 

0 : displays no keyboard

1: displays the popup keyboard (online for system popup windows)

2: displays only the main keyboard (doesn't work in popups)

3: displays both 

 

They are separate keyboards because of the way Windows handles modality.  If that makes no sense, don't worry about.  Just set it = 3.

Link to comment
Share on other sites

  • 2 years later...

Having same issue.  Set System.OnScreenKeyboard = 2 (or 3) for use with System.EntryDialog().  Activate the dialog, dialog and keyboard appear, whatever is typed on the keyboard doesn't appear in the dialog entry.  Strangely, for numeric entries, even though nothing appears, if I try to click "OK" on the dialog, it errors out on the max allowed value, so it seems as if something is getting sent from the onscreen keyboard, not visible, that the entry dialog thinks is larger than the max allowed input.  Odd.

I've used this successfully before (on an earlier version of DF and Windows), but don't remember what I did differently, if anything.  Ideas?

Link to comment
Share on other sites

  • 1 month later...

So working on building my own keyboard (number pad in this case) with the Symbol Factory keycaps.  Built the actual keypad, store each keypress by appending to a string (some with some logic like no leading zeros, no more than one decimal point, etc)

I'm creating a numeric entry function that will use the the keypad as a popup and return the entered value.  So in the sequence I do whatever pre-popup work needs be done, and then pop up the keypad, no problem so far.  But then I have to wait for the operator to be done entering/editing his value, before I can return the value to the calling sequence.  I tried using Waitfor(Page.strCurrentPage != "MyKeypad"), but Page.strCurrentPage doesn't update for popups and it just returns the passed in default value.  Tried clearing an entry-done flag in the sequence, then the Enter key on the keypad sets the flag, and using  Waitfor(EntryComplete == 1) before returning, but that seems to hang DF and require me to kill the program and restart.

How do people handle program flow control for user designed keypads/keyboards?

Link to comment
Share on other sites

Hmmm....thought maybe it was a thread issue, so I tried putting the PopupModal() in a second sequence and calling beginseq(), but it acts the same.  Besides, I know PopupModal() isn't blocking for background sequence execution because that's what I'm trying to fix:  With nothing blocking in the sequence, it just merrily rolls past the PopupModal() and returns the value when there isn't one to return yet.  But if I put something blocking/looping in the sequence, it hangs and kills DF.  Read so far as I know everything in the User Guide about PopupModal(), and I can't see any reason this shouldn't work.

Link to comment
Share on other sites

The hang might be because you forgot the second parameter of waitfor().  But it should have given you a compilation error.  The second parameter tells DAQFactory how often it checks the condition.  If that's not it, then please post a very simple example of it hanging and I'll quickly be able to tell what's wrong.

That all said, I usually do keypads with a callback.  So, there are two global string variables.  One contains the contents entered into the keypad (and the initial contents if desired).  The other contains a line of script that gets executed when the user hits Enter.  So, you might have:

global string popupValue
global string popupCallback

and then the action for the Enter "key" is:

popupValue = component.myEdit.strContents
page.myPage.ClosePopup()
execute(popupCallback)

Then if you want to use the popup to set an output on a channel, you can set popupCallback just to:

popupCallback = "myChannel = popupValue"

Or if you want to call a function and pass that value, set it to:

popupCallback = "myFunc(popupValue)"

etc.

BTW: you also might consider doing the popup without actually creating a popup, using one of two ways.  One way is to overlay the page with a nice Panel background on the keypad.  The other is to simply switch to another page.

To do these sort of things, take advantage of the push and pop() functions of a string variable.  Push() will add the given string to the end of the array.  Pop() will return the last string in the array and remove it from the array in the same step.  So you can:

myPageList.push(page.strCurrentPage)

before you set it to something else:

page.strCurrentPage = "popupPage," + page.strCurrentPage

then pop() it when done:

page.strCurrentPage = myPageList.pop()

Of course push() and pop() work for numeric arrays too.

Note that the overlay page with a panel only works with newer versions of DAQFactory.  Older versions would always draw panels and graphs behind everything else.

 

Link to comment
Share on other sites

Oh yeah, sorry about that.  The Waitfor()s in my sequence did have an interval specified.

I'm trying out the overlay idea, but I'd really like to get the entry back as a return value.  It seems a lot cleaner at the point where the numeric entry is used (plus less code to duplicate -- it only occurs once, in the entry function, rather than with every call).

Anyhow, bottom line, I still have the odd hang if I follow the display of the new screen with a wairfor().  With the waitfor() commented out, I get my default returned immediately.  Add the waitfor() and it hangs DF.  I'm baffled.

 

function NumEntry(string Prompt, RangeMin, RangeMax, Default)
   EntryActive = 1
   global EntRangeMin
   global EntRangeMax
   global string NumericEntry
   if(IsEmpty(RangeMin))
         EntRangeMin = -2e10
      else
         EntRangeMin = RangeMin
      endif
   if(IsEmpty(RangeMin))
         EntRangeMax = 2e10
      else
         EntRangeMax = RangeMax
      endif
   if(IsEmpty(Default))
         NumericEntry = ""
      else
         NumericEntry = Default
      endif
Page.strCurrentPage = "NumKeyboard," + Page.strCurrentPage
Waitfor((Find(Page.strCurrentPage, "NumKeyboard", 0) == -1), 100)
return(NumericEntry)

 

Link to comment
Share on other sites

Works perfectly for me, at least once I change the 100 in your waitfor() to 0.1.  The units are seconds, so 100 means it only evaluates every 100 seconds.  Either way it doesn't hang DAQFactory.

Note that if you call your NumEntry() function from a component action or anything else that runs in the primary application thread, it will hang DAQFactory.  You can only do a waitfor() like that inside a sequence thread started with beginseq() or similar.

Link to comment
Share on other sites

Right, so that is why it hangs.  The waitfor() is being called from the primary application thread inside an event handler and you are blocking so DAQFactory can't process any other events (like mouse clicks, etc) so appears hung.  In fact, its still running other sequences in the background, its just the UI that is hung.  You cannot create a blocking function like that in the primary thread, i.e. UI element Actions or the Command / Alert.

The only reason entryDialog() works is because internally we are actually processing events while waiting for the dialog.

Link to comment
Share on other sites

Sorry, you replied before I got a chance to check something.

In order to do block inside a component Action or Command / Alert call, you have to trigger the message pump.  We don't do this automatically in waitfor() or other blocking calls because there is a performance penalty, but in your case, you don't care and have to pump, which is why we created the system.messagePump() function.

Anyhow, replace your waitfor() line with this:

while(Find(Page.strCurrentPage, "NumKeyboard", 0) != -1)
      system.messagePump()
      delay(0.01)
 endwhile

And it should work the way you want.  

system.messagePump() is not documented in the user's guide.

 

Link to comment
Share on other sites

Your solution is cool, because it allowed me to go back to using a popup, which I prefer.  I like the modality, because then I don't have to worry about the user selecting a second numeric entry while one is already open, having to position the popup keypad so no mouse clicks can fall through to the base screen and click on a button unintentionally, etc.

Since I'd like this to be a standard option for all my projects, on the level of setting System.OnScreenKeyboard, I changed the call in the numeric entry fields from System.EntryDialog() to my own EntryDialog() function, which then does the user keypad or System.EntryDialog depending on the value of global variable UseOnScreenKeyboard.  So in EntryDialog() I have

function EntryDialog(string Prompt, RangeMin, RangeMax, Default)
   if(IsEmpty(RangeMin))
         EntRangeMin = -2e10
      else
         EntRangeMin = RangeMin
      endif
   if(IsEmpty(RangeMax))
         EntRangeMax = 2e10
      else   
         EntRangeMax = RangeMax
      endif
   if(IsEmpty(Default))
         NumericEntry = ""
      else
         NumericEntry = Default
      endif
   if(UseOnScreenKeyboard)
      EntryActive = 1
      Page.NumKeyboard.PopupModal()
      while(EntryActive == 1)
         System.MessagePump()
         delay(0.01)
         endwhile
      if(NumericEntry == "")
            return null
         else   
            return(StrToDouble(NumericEntry))
         endif
      else
         return System.EntryDialog(Prompt, EntRangeMin, EntRangeMax, NumericEntry)
      endif

In the Enter button on the keypad I have

Private ReturnTemp = StrToDouble(NumericEntry)
switch
   case (ReturnTemp == Nan())
      EntryActive = 0
      Page.NumKeyboard.ClosePopup()
   case (ReturnTemp > EntRangeMax)
      System.MessageBox("Entry must be no more than " + EntRangeMax)
   case (ReturnTemp < EntRangeMin)
      System.MessageBox("Entry must be at least " + EntRangeMin)
   case (1)
      EntryActive = 0
      Page.NumKeyboard.ClosePopup()
   endcase

If possible, I'd like to move the mouse click handling into the EntryDialog() function so that all the logic is in one place and because it would eliminate the intermediate global variables EntRangexxx, which are only there because UI objects in the popup can't see the sequence private variables.  Also, because I could then convert the keypad into a single graphic, which would make for easier resizing on apps at different resolutions.  Since OnLButtonDown() is a top level function, is there any way to do get the mouse handling in here, that will result in less duct tape and baling wire than what I have here?

PS:   The min and max args are still swapped in the System.EntryDialog() pop up prototype help.  (Also, the range check responses in System.EntryDialog are a little wonky.  An entry less than the minimum returns "value must be greater than" the minimum, rather than "greater than or equal to" or "at least", and similarly for the maximum.)

Thanks!

Link to comment
Share on other sites

 

Are declarations time consuming enough to justify doing something like this at the beginning of the sequence that has the function?  I'd like it to all be as portable as possible, but my normal approach is to put declarations in a one-time startup sequence, so they don't get called with each and every function call.

if(IsEmpty(initialized))
   global EntryActive = 0
   global EntRangeMin
   global EntRangeMax
   global string NumericEntry   
   static initialized = 1
   endif

 

Link to comment
Share on other sites

If you really want to make it portable, put it all in a class.  You can even instantiate the class in the same sequence that declares it, or leave that to the user of the class to put in a start up sequence.

But really declarations are not expensive.  The most expensive thing in DAQFactory is actually a function call to a user function.  Calls it internal ones are fast, but ones to user ones are relatively slow.  This is, however, all relative, and only an issue if you are going to call it a lot.  Its certainly not an issue in a user interface function.

BTW: you don't need to use the "initialized" variable in your script.   Just do isEmpty() on EntryActive since until you declare it in the second line, it is empty.  IsEmpty() returns 1 if a variable has been declared, but hasn't been initialized (or was set to null), but it also returns 1 if the variable just plain doesn't exist yet.

 

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.