Home   Archive   Permalink



'Wait for key press' in Rebol 2.7

I want to wait for a key press before changing and showing a predefined window. Any key will do. What I've tried is below. It sits and waits, not recognizing keyboard input.
    
What is wrong with this approach?
    
Pete
    
    
R E B O L []
view my-layout: layout [
    size 100x100
    text-block: text "first"
    ]
    
my-layout/feel [
    engage: func [f a e] [
        until a = 'key
        ]
    ]
    
text-block/text: "second"
show text-block
    
wait 2


posted by:   Pete       25-Apr-2014/14:03:54-7:00



I'd do it like this:
    
R E B O L []
insert-event-func func [f e] [
     if e/type = 'key [
         text-block/text: "second"
         show text-block
     ] e
]
view layout [
     size 100x100
     text-block: text 100 "first"
]

posted by:   Nick       25-Apr-2014/14:16:16-7:00



Nick,
    
Hmmm. Because the actual program I'm working on has its own 'switch (one branch of which results in the "wait for key press"), I need a method that doesn't require the next line executed to be a result of finding the 'if conditional to be true. It needs to--er--stand alone.
    
Make sense? Can it be done?
    
Pete

posted by:   Pete       25-Apr-2014/15:50:55-7:00



I also manipulate the viewed layout prior to needing the key trap, so I can't get away with having the layout definition second. Sorry if I wasn't clear. It needs to be three clean parts: action a; wait for keyboard input; action b, action b not nested in a conditional.
    
Thanks for all your help!

posted by:   Pete       25-Apr-2014/16:24:37-7:00



You can 'insert-event-func at any time, even inside a switch construct, and it runs kinda like a separate thread. The 'if conditional in it doesn't block any other program flow - it just does the enclosed block if a key event occurs (it doesn't stop the rest of the program flow, by waiting, it just does the specified code if at any point a key is pressed). You can run the rest of your code, and use 'remove-event-func to remove whatever handler you have (label the insert-event-func code first).
    
If you need more help, please post more of the actual code structure you're working with.

posted by:   Nick       26-Apr-2014/1:00:41-7:00



Nick,
    
Thanks for your patience. The problem is that I do need to halt execution. I'm currently using 'wait, but I don't get the exact timing I could get if I could control flow manually. This is important because execution of the program is to accompany a vocal presentation.
    
I am using 'forall to pass through a block that's been retrieved from a text file. Each time through, I use 'switch to parse image filenames, text-block locations, and text to display. The program does the display work in real time while passing through the script. It's working exactly as I want it to, except I've had to use 'wait to pause execution at the instant when the "wait for key stroke" is called for.

posted by:   Pete       26-Apr-2014/8:44:59-7:00



I've been trying to put a 'forever loop in place, like the one you (is this you?) offer here (http://www.rebol.org/art-display-article.r?article=qmc74z, starting at "That little bit of code"). It doesn't throw an error, but neither does it respond to keyboard input, in place in my program.
    
Here's what I did:
    
keys: open/binary/no-wait [scheme: 'console]
forever [
    if not none? wait/all [keys :00:00.01] [break]
]
    
What gives?

posted by:   Pete       26-Apr-2014/10:12:40-7:00



Oops. Here's the clickable link: http://www.rebol.org/art-display-article.r?article=qmc74z

posted by:   Pete       26-Apr-2014/10:23:24-7:00



The console doesn't have focus to accept keystrokes if you don't show it:
    
R E B O L []
print ""
keys: open/binary/no-wait [scheme: 'console]
forever [
     if not none? wait/all [keys :00:00.01] [
         switch to-string to-char to-integer copy keys [
             " " [print "you pressed 'space'"]
         ]
     ]
; print "nothing pressed" ; make sure it's working
]

posted by:   Nick       26-Apr-2014/12:19:30-7:00



If you don't want to show a console, you could hide a window off screen, attach a key to a widget and do what you want:
    
view/offset layout [btn #" " [alert "pressed" quit]] -100x-100
    
Or if you want to affect some widget on screen with a key stroke, hide the widget with the key attached:
    
view layout [
     size 100x100
     text-block: text 100 "first"
     at -100x-100 btn #" " [
         text-block/text: "second"
         show text-block
     ]
]
    
Again, if none of those examples do what you need, please post some of your actual working code structure, and I can help you more directly.

posted by:   Nick       26-Apr-2014/12:25:58-7:00



I thought we had our solution with the hidden offscreen window, but no. I am sorry to ask this, but would you mind terribly if I sent you the code via email? Not sure I want it sitting on the forum 'forever.

posted by:   Pete       26-Apr-2014/13:57:48-7:00



Focus needs to stay on the main-and-only viewed window (no console), and execution needs to be paused (as with 'wait or 'forever) until there is keyboard input of any kind (specifying a carriage return would also work for my purposes).

posted by:   Pete       26-Apr-2014/14:04:16-7:00



What if I added this line to my layout definition, to make an off-screen button with a keyboard shortcut (slight variation on one of your suggestions)?
    
hidden-button: btn -100x-100 #"^/" [proceed: true]
    
Then, each time through the forall loop, if the switch condition is met and I want to pause, I include this:
    
proceed: false
forever [if proceed = true [break]]
    
The 'switch function waits for the off-screen button to change the global variable to true before proceeding.
    
What do you think?

posted by:   Pete       26-Apr-2014/17:13:49-7:00



I had a little snippet which did such a thing - if it suits your needs, 1 harmless global shouldn't hurt anyone :)

posted by:   Nick       26-Apr-2014/19:01-7:00



I can't help you with this problem, but I spotted this in your question: "This is important because execution of the program is to accompany a vocal presentation." Could you solve the problem by avoiding it, with something like this:    
    
http://business-programming.com/business_programming.html#section-9


posted by:   Steven White       28-Apr-2014/9:30:29-7:00



Thanks, Steven. I have a complete program that does everything I need except wait for a keystroke once per cycle, so I'm not keen to abandon ship just yet. : )
    
The code below should display "A" in a window until the button (or the shortcut key, "x") is pressed. It should then change the displayed text to "B", wait 1 second, and then change the displayed text to "C" for 2 seconds.
    
Instead, it crashes REBOL (hard). Can anyone tell me why?
    
    
R E B O L []
    
view/new layout [
    a: text "A"
    btn #"x" [
        a/text: "B"
        show a
    ]
]
    
until [a/text = "B"]
wait 1
a/text: "C"
show a
    
wait 2

posted by:   Pete       28-Apr-2014/10:03:43-7:00



If anybody wants to write a 'wait/keystroke refinement that waits until keyboard input is detected before continuing execution, I'll overnight them a freshly-baked batch of chocolate chip cookies.
    
Seriously.
    
Barring that, I guess I'll write an alternate version of my program that uses a timing script that's generated in another (console-only) program. Not ideal, but it will at least eliminate the "clackety" sound that would have been a hazard in the audio recordings.

posted by:   Pete       28-Apr-2014/11:28:17-7:00



Without looking at more of your code structure, we're running around in circles working on things that aren't specifically what you need.
    
One of the points clearly misunderstood, by the code above, is that GUIs generally run in an event loop of some sort. If you use view/new, and never 'do-events, then you must perform a loop manually (typically with 'forever). If you don't use /new, then the GUI runs in an event loop automatically, and events are handled by some sort of event handler, such as the key handler above, by a handler installed using 'insert-event-func, by using 'feel, etc. In the console, you can use the code provided earlier, to handle keystroke events. Your code above never starts any sort of loop.
    
Based on the code above, what you need is this:
    
R E B O L []
view layout [
     a: text "A"
     btn #"x" [
         a/text: "B"
         show a
         wait 1
         a/text: "C"
         show a    
         wait 2
         unview
     ]
]
    
Everything you're trying to do, while in a GUI needs to be included in a handler of some sort, and there's clearly some confusion about where and how those handlers need to be implemented.
    
Even if you're creating code which involves running multiple tasks (and loops) concurrently and syncing them in some way, the options we've written out will cover you.
    
Before I can clarify a solution any further, I need to see what you're tying to actually do. You've described your intent to a degree, but without some more of the actual code structure you're attempting to organize, I expect this is going to continue to miss the mark. I'm happy to give you the answer, but I need to see the actual code question more clearly.

posted by:   Nick       28-Apr-2014/13:20:26-7:00



It would be very difficult for you to understand this without seeing the code, of course, but my simplifications are quite to-the-point. Make one of them work, the larger program will work.
    
It's all right. I have moved on to the "timing script" model described in my last post. It offers the great advantage of my not having to physically minimize keyboard noise with sound-proofing, etc. Worth it, I think.
    
That said, I'm sure I'm not alone in thinking that having a /keypress refinement for 'wait would be useful. Keyboard input could be routed to a port, and 'wait could wait for input as it does for file i/o. The offer of overnighted cookies stands. : )
    
Cheers,
Pete

posted by:   Pete       28-Apr-2014/16:25:40-7:00



It would be very difficult for you to understand this without seeing the code, of course, but my simplifications are quite to-the-point. Make one of them work, the larger program will work.
    
It's all right. I have moved on to the "timing script" model described in my last post. It offers the great advantage of my not having to physically minimize keyboard noise with sound-proofing, etc. Worth it, I think.
    
That said, I'm sure I'm not alone in thinking that having a /keypress refinement for 'wait would be useful. Keyboard input could be routed to a port, and 'wait could wait for input as it does for file i/o. The offer of overnighted cookies stands. : )
    
Cheers,
Pete

posted by:   Pete       28-Apr-2014/16:25:43-7:00



Take a look at this:
    
R E B O L []
texts: ["B" "C" "D"]
c: 0
view layout [
     a: text 200 "A"
     at -100x-100 key #"x" [
         c: c + 1
         a/text: pick texts c
         show a
     ]
]
    
For anyone watching and thinking about this, it's worth finishing up. The event loop is what does the waiting (not a separate function). When the event occurs (automatically waited for because the event loop is constantly iterating), the action you intend then gets performed. It's the action, in our communication which I haven't been understanding clearly. In this case, if I get it right, the intent is to update the text displayed by the 'a widget.
    
You could also use the code below to scroll forward or backward through the texts on demand (this example uses the up and down arrow keys to go forward and backward):
    
R E B O L []
texts: ["A" "B" "C" "D"]
c: 1
view layout [
     a: area (texts/1)
     at -100x-100 key keycode 'up [
         c: c + 1
         a/text: pick texts c
         show a
     ]
     at -100x-100 key keycode 'down [
         c: c - 1
         a/text: pick texts c
         show a
     ]
]
    
If I misunderstand your intent, the code technique will still involve using the event loop to do the waiting, as opposed to a separate function.

posted by:   Nick       28-Apr-2014/19:15:42-7:00



I really want those cookies :)

posted by:   Nick       28-Apr-2014/19:17:40-7:00



You could wrap the intended actions in a function, if they're complex (but in the case above there's just a wee bit of duplicated code):
    
R E B O L []
texts: copy []
repeat x 7 [append texts form copy/part at extract svv/vid-styles 2 (x - 1 * 10) 10]
c: 1
scrll: func [dir] [
     either dir = "up" [c: c + 1] [c: c - 1]
     a/text: pick texts c
     show a
]
view layout [
     a: area wrap (texts/1)
     at -100x-100 key keycode 'up [scrll "up"]    
     at -100x-100 key keycode 'down [scrll "down"]
]

posted by:   Nick       28-Apr-2014/19:40:23-7:00



Certainly some interesting directions you've taken this, Nick! I've just finished the program that raised the original questions, and I'll need to take a closer look when my brain isn't crawling along at about 4%. : )

posted by:   Pete       29-Apr-2014/0:08:50-7:00



It sounds like you have a solution, but one thought came to mind.
    
If you could get by NOT with responding to ANY key, but with responding to a PARTICULAR key, then you could put on your layout a button with a size of 0x0, and set a keyboard shortcut for it. You actually would be clicking a button, but it wouldn't look like it because nobody could see the button:
    
R E B O L []
    
RESPOND-TO-X: func [TEXTVALUE] [
     switch TEXTVALUE [
         "A" [set-face a "B" show a]
         "B" [set-face a "C" show a]
     ]
]
    
view layout [
     a: text "A"
     btn #"x" 0x0 [RESPOND-TO-X a/text]
]
    
alert "done

posted by:   Steven White       29-Apr-2014/10:48:36-7:00