Reading user input without halting execution - events

I'm trying to read user input in a console interface in a Lua application.
However, I do not want to halt program execution as the title suggests. Using read() would interfere with the normal handling of ongoing events, and interface updates, while the user's typing.
The best solution I could come up with was maintaining a temporary variable I apply any user input to that isn't a function key. However, my event handler returns scancodes, and I know of no way to map these to ASCII codes short of maintaining a table for each key, which is something I really want to avoid.
Any ideas?
Edit
To give an example of what I mean:
function read_input()
local input = read()
do_something_with(input)
end
while true do
e,param1,param2,param3 = os.pullEvent()
if (e=='key' and param1=='201') then
read_input()
elseif (e=='something_else') then
do_something_else()
end
end
As you can see, the user can at some point land in a function demanding user input. While this input is taken, I can't have any other (event-based) functions of the program being impeded by this.

You would need to read the keys continuously from the os.pullEvent()
local input
while true do
e,param1,param2,param3 = os.pullEvent()
if (e=='key') then
-- do some fancy stuff for checking which key has pressed and append it to input
-- additionally if it's enter do something other fancy
else
-- blah blah
end
end
I'm not sure but I thought the character of which key was pressed is in one of the params of the pullEvent, I might be mistaken tho.
Additionally if you want to do something every second or so, start a timer which also fires the pullEvent.

Related

How to display debug info or console.log equivalent in Lua

I am creating many games using Lua and LOVE2D, but whenever I implement a new function and want to test it out, or simply want to know a value of a variable in Lua, I either display it on the game screen or just hope that it works.
Now my question is...
IS THERE A WAY TO DISPLAY SOME INFO, such as A VARIABLE VALUE or something else into the terminal or somewhere else? Just like console.log in javascript which displays some content in the javascript console in the browser. So, is there a way to do this is Lua?? using LOVE2D?
I am using a Mac, so I have a terminal and not a command prompt. Is there a way to display some content there? Anywhere else would also be fine, I just need to see if those values are as expected or not.
Use a conf.lua file to enable the console, then you should be able to use a standard print(). You can read the wiki entry here.
Note: You have to run Lua and Love2D via the terminal for this to work. Running Lua and Love2D like this is required for the print statements to show:
/Applications/love.app/Contents/MacOS/love "/Users/myuser/Desktop/love2d-test-proj"
You just need to add a conf.lua file to the same location where your main.lua. Your file may be as simple as this:
function love.conf(t)
t.console = true
end
But feel free to copy the whole configuration file from the above link and edit what you need.
I can't be completely sure about this, because I have no access to Mac, but the console is disabled by default and even on Windows, no prints are shown until you turn it on.
Alternatively You can also display debug info in the game itself like some games do.
What I like to do is add something like debugVariable = {} for logging events that happen in each loop and debugPermanent = {} for events that happen rarely. Possibly add convenience functions for writing to the variables:
function debugAddVariable(str)
table.insert(debugVariable, str)
end
--..and similarly for debugPermanent
Now a function to draw our debug info:
function debugDraw()
love.graphics.push() --remember graphics state
love.graphics.origin() --clear any previous transforms
love.graphics.setColor(--[[select color for debug info]])
love.graphics.setFont(--[[select font for debug info]])
for i, v in ipairs(debugPermanent) do
love.graphics.print(v)
love.graphics.translate(0, --[[fontHeight]])
end
for i, v in ipairs(debugVariable) do
love.graphics.print(v)
love.graphics.translate(0, --[[fontHeight]])
end
debugVariable = {} --clear debugVariable to prepare it for the next loop
love.graphics.pop() --recall graphics state
end
And we just call this draw function at the end of our love.draw() and the texts should appear.
Obviously, this method can be refined further and further almost infinitely, displaying specific variables, and adding graphs for some other variables to clarify the information you want to show, but that's kind of outside of the scope of the question.
Lastly Feel free to check here for debug libraries submitted by users.

Reading barcode in datawindow (powerbuilder)

I have like a POS application where I work, and I added new code to read barcodes in a datawindow, but been having some problems to use it correctly. The process needs to read the barcode and automatically search for the product, and insert it in another datawindow with the info regarding the product.
The problem is that I have the code in the editchanged event, and that the barcodes have different lenght; so, every time a number of the barcode is read, the search function is called, here is the code for that function:
ll_rtn = dw_search.Retrieve()
If ll_rtn = 1 Then
is_code = dw_search.GetItemString(1,'barcode')
wf_insert(False)
End If
If for example, a product of 10 digits is read and at length 5 the product is found, it's inserted but the editchanged is still running because the barcode is length 10, and depends of the search, might find it again with the last digits and insert it again.
I don't know how to stop reading after I find the product. Hope I explain it right.
Can you check if the barcode scanner can be configured to pass carriage return / Enter, upon scanning the barcode and this way you can move your code to itemchanged event
Bar Code Config
An editchanged event is fired for each keystroke in the control. You probably want to either move the code to the itemchanged event or to a totally separate event which is posted from the itemchanged event.
If you absolutely have to have the code in the editchanged event you need to set a boolean instance variable on the window which is set TRUE once the product is found. If it's true then don't search. Remember to reset the variable to FALSE prior to any new search.

Validating a PIN entered by users

How would you refactor this code that checks if users have added a 4+ digit PIN so the code will be as short as possible?
def has_pin?
return self.pin.to_i > 1000 rescue nil
false
end
Ideally without using AR validation on user creation time as they're created with no PIN at first, but may choose to add them later in some specific cases.
PS: the PIN is stored as string for some reason.
I would go with:
def pin_valid?
pin.present? && pin.match?(/\A\d{4,}\z/)
end
Why do you aim to have your code as short as possible? I would always aim to have my code as easy to understand as possible.

What is the best way to get keyboard events (input without press 'enter') in a Ruby console application?

I've been looking for this answer in the internet for a while and have found other people asking the same thing, even here. So this post will be a presentation of my case and a response to the "solutions" that I have found.
I am such new in Ruby, but for learning purposes I decided to create a gem, here.
I am trying to implement a keyboard navigation to this program, that will allow the user use short-cuts to select what kind of request he want to see. And in the future, arrow navigations, etc.
My problem: I can't find a consistent way to get the keyboard events from the user's console with Ruby.
Solutions that I have tried:
Highline gem: Seems do not support this feature anymore. Anyway it uses the STDIN, keep reading.
STDIN.getch: I need to run it in a parallel loop, because at the same time that the user can use a short-cut, more data can be created and the program needs to show it. And well, I display formated text in the console, (Rails log). When this loop is running, my text lost the all the format.
Curses: Cool but I need to set position(x,y) to display my text every time? It will get confusing.
Here is where I am trying to do it.
You may note that I am using "stty -raw echo" (turns raw off) before show my text and "stty raw -echo" (turns raw on) after. That keeps my text formated.
But my key listener loop is not working. I mean, It works in sometimes but is not consistent. If a press a key twice it don't work anymore and sometimes it stops alone too.
Let me put one part of the code here:
def run
# Two loops run in parallel using Threads.
# stream_log loops like a normal stream in the file, but it also parser the text.
# break it into requests and store in #requests_queue.
# stream_parsed_log stream inside the #requests_queue and shows it in the screen.
#requests_queue = Queue.new
#all_requests = Array.new
# It's not working yet.
Thread.new { listen_keyboard }
Thread.new { stream_log }
stream_parsed_log
end
def listen_keyboard
# not finished
loop do
char = STDIN.getch
case char
when 'q'
puts "Exiting."
exit
when 'a'
#types_to_show = ['GET', 'POST', 'PUT', 'DELETE', 'ASSET']
requests_to_show = filter_to_show(#all_requests)
command = true
when 'p'
#types_to_show = ['POST']
requests_to_show = filter_to_show(#all_requests)
command = true
end
clear_screen if command
#requests_queue += requests_to_show if command
command = false
end
end
I need a light in my path, what should I do?
That one was my mistake.
It's just a logic error in another part of code that was running in another thread so the ruby don't shows the error by default. I used ruby -d and realized what was wrong. This mistake was messing my keyboard input.
So now it's fixed and I am using STDIN.getch with no problem.
I just turn the raw mode off before show any string. And everything is ok.
You can check here, or in the gem itself.
That's it.

What's so bad about using button captions as variables in VB6?

I received some justified critical feedback on my last question (How to gracefully exit from the middle of a nested subroutine when user cancels?) for using the caption of a command button as a state variable. I did it because it's efficient, serving two or three purposes at once with very little code, but I understand how it could also cause problems, particularly in the slightly sloppy way I originally presented it.
I feel like this deserves its own discussion, so here's the same idea cleaned up a bit and modified to do it "right" (which basically means defining the strings in a single place so your code won't start failing because you simply changed the text of a command button). I know my variable and control naming convention is poor (OK, nonexistent), so apologies in advance. But I'd like to stay focused on the caption as state variable discussion.
So here we go:
' Global variables for this form
Dim DoTheThingCaption(1) As String
Dim UserCancel, FunctionCompleted As Boolean
Private Sub Form_Initialize()
' Define the possible captions (is there a #define equivalent for strings?)
DoTheThingCaption(0) = "Click to Start Doing the Thing"
DoTheThingCaption(1) = "Click to Stop Doing the Thing"
' Set the caption state when form initializes
DoTheThing.Caption = DoTheThingCaption(0)
End Sub
Private Sub DoTheThing_Click() ' Command Button
If DoTheThing.Caption = DoTheThingCaption(0) Then
UserCancel = False ' this is the first time we've entered this sub
Else ' We've re-entered this routine (user clicked on button again
' while this routine was already running), so we want to abort
UserCancel = True ' Set this so we'll see it when we exit this re-entry
DoTheThing.Enabled = False 'Prevent additional clicks
Exit Sub
End If
' Indicate that we're now Doing the Thing and how to cancel
DoTheThing.Caption = DoTheThingCaption(1)
For i = 0 To ReallyBigNumber
Call DoSomethingSomewhatTimeConsuming
If UserCancel = True Then Exit For ' Exit For Loop if requested
DoEvents ' Allows program to see GUI events
Next
' We've either finished or been canceled, either way
' we want to change caption back
DoTheThing.Caption = DoTheThingCaption(0)
If UserCancel = True Then GoTo Cleanup
'If we get to here we've finished successfully
FunctionCompleted = True
Exit Sub '******* We exit sub here if we didn't get canceled *******
Cleanup:
'We can only get to here if user canceled before function completed
FunctionCompleted = False
UserCancel = False ' clear this so we can reenter later
DoTheThing.Enabled = True 'Prevent additional clicks
End Sub '******* We exit sub here if we did get canceled *******
So there it is. Is there still anything really that bad about doing it this way? Is it just a style issue? Is there something else that would give me these four things in a more desirable or maintainable way?
Instant GUI feedback that user's button press has resulted in action
Instant GUI feedback in the location where user's eyes already are on how to CANCEL if action is not desired
A one-button way for users to start/cancel an operation (reducing the amount of clutter on the GUI)
A simple, immediate command button disable to prevent multiple close requests
I can see one concern might be the close coupling (in several ways) between the code and the GUI, so I could see how that could get to be a big problem for large projects (or at least large GUIs). This happens to be a smaller project where there are only 2 or 3 buttons that would receive this sort of "treatment".
The single biggest problem with this technique is that it uses a string as a boolean. By definition, a boolean variable can have only two states, while a string can have any number of states.
Now, you've mitigated the danger inherent in this somewhat by relying on an array of predefined strings to define allowed values for the command button text. This leaves a handful of lesser issues:
Logic is less-than-explicit regarding current and available states (there are actually four possible states for the form: not-started, started, completed, started-but-canceling) - maintenance will require careful observation of the potential interactions between button text and boolean variable states to determine what the current state is / should be. A single enumeration would make these states explicit, making the code easier to read and understand, thereby simplifying maintenance.
You're relying on the behavior of a control property (button text) to remain consistent with that of the exposed property value type (string). This is the sort of assumption that makes migrating old VB6 code to newer languages / platforms absolute hell.
String comparison is much, much slower than a simple test of a boolean variable. In this instance, this won't matter. In general, it's just as easy to avoid it.
You're using DoEvents to simulate multi-threading (not directly relevant to the question... but, ugh).
The biggest issue i've come accross when working on (very old) code like this [button captions as variables] is that globalisation is a nightmare.... I had to move a old vb6 app to use English and German... it took weeks, if not months.
You're using goto's as well..... a bit of refactoring needed perhaps to make the code readable??
**Edit in response to comments
I'd only use a goto in vb6 at the top of each proc;
on error goto myErrorHandler.
then at the very bottom of the proc i'd have a one liner that would pass err to a global handler, to log the error.
Ignoring the general architecture/coupling problems because you are aware of those issues, one problem with your approach is because VB6 controls do magic stuff when you set properties.
You may think you are just setting a property but in many cases you are causing events to fire also. Setting a checkbox value to true fires the click event. Setting the tabindex on a tab control causes a click event. There are many cases.
If I remember correctly I also think there are scenarios where if you set a property, and then read it immediately, you will not see the update. I believe a screen refresh has to occur before you see the new value.
I have seen too much horrible VB6 code that uses control properties as storage. If you ever find this kind of code you will recognize it because it is scattered with redundant calls to Refresh methods, DoEvents and you will frequently see the UI get hung. This is often caused by infinite loops where a property is set, an event is fired and then another property is set and eventually someone writes a line of code that updates the first property again.
If those issues don't scare you enough then think of this. Some of us just are not that smart. I've been coding in VB6 for over 10 years and have personally written probably around 750K LOC and I keep staring at your example above and I find it very difficult to understand what it going on. Assume that all the people that will need to read your code in the future will be really dumb and make us happy by writing really simple looking code.
I think it's better to decouple the caption text from the state of processing. Also the goto's make it hard to read. Here is my refactored version...
Private Const Caption_Start As String = "Click to Start Doing the Thing"
Private Const Caption_Stop As String = "Click to Stop Doing the Thing"
Private Enum eStates
State_Initialized
State_Running
State_Canceled
State_Completed
End Enum
Private Current_State As eStates
Private Sub Form_Initialize()
DoTheThing.Caption = Caption_Start
Current_State = State_Initialized
End Sub
Private Sub DoTheThing_Click()
If Current_State = State_Running Then
'currently running - so set state to canceled, reset caption'
'and disable button until loop can respond to the cancel'
Current_State = State_Canceled
DoTheThing.Caption = Caption_Start
DoTheThing.Enabled = False
Else
'not running - so set state and caption'
Current_State = State_Running
DoTheThing.Caption = Caption_Stop
'do the work'
For i = 0 To ReallyBigNumber
Call DoSomethingSomewhatTimeConsuming
'at intervals check the state for cancel'
If Current_State = State_Canceled Then
're-enable button and bail out of the loop'
DoTheThing.Enabled = True
Exit For
End If
DoEvents
Next
'did we make it to the end without being canceled?'
If Current_State <> State_Canceled Then
Current_State = State_Completed
DoTheThing.Caption = Caption_Start
End If
End If
End Sub
Apart from removing the GOTos as DJ did in his answer, there is nothing really wrong about your approach. The button caption can have only two states, and you use those two states to define the flow in your code.
I have however two reasons why I would do it differently:
Your method creates problems when you want to translate your program into a different language (in my experience you should always plan for that), because the captions would change in another language
It goes against the principle of seperating the user interface from the program flow. This may not be an important thing for you, but when a program gets bigger and more complex, having a clear seperation of the UI from the logic makes things much easier.
To sum it up, for the case at hand your solution certainly works, and there is no reason why it shouldn't. But on the other hand experience has taught us that with more complex programs, this way can cause problems which you can easily avoid by using a slightly different approach.
Also, I think it is safe to assume that everybody who criticised your example did so because they made a simnilar choice at some point, and later realised that it was a mistake.
I know I did.
This ties your underlying algorithm to specific behavior in your UI. Now, if you want to change either one of them, you have to make changes to both. As your app grows in size, if you don't keep your changes local by encapsulating logic, maintenance will become a nightmare.
If anyone for any reason ever needs to work on your code, they won't find practices and conventions they are familiar and comfortable with, so the boundaries of functionality won't exist. In other words, you are headed in the wrong direction on the Coupling/Cohesion trail. Functionally integrating State management with the UI is the classic poster child for this issue.
Do you understand OOP at all? (Not a criticism, but a legitimate question. If you did, this would be a lot clearer to you. Even if it's only VB6 OOP.)
Localization has the biggest impact on the type of logic OP is presenting. As several people mentioned it - what if you need to translate the app into Chinese? And German? And Russian?
You'd have to add additional constants covering those languages too... pure hell. GUI data should remain what it is, a GUI data.
The method OP describes here reminded me what Henry ford said: "Any customer can have a car painted any color that he wants so long as it is black".

Resources