Perspective Barcode Scanner Input: Manual Entry

What would be the best way to allow users to manually type a barcode in then trigger the barcode scanner input’s onActionPerformed script? The component is using a regex pattern instead of prefix/suffix.

The view will be run on a hand-held without a physical keyboard, so some sort of other component or action will need to trigger the screen keyboard. Also, type so the user can see the input before submitting the barcode.

I wonder if you could use a text input then on commit, do virtual keypresses to buffer into the barcode scanner input?

The best way would probably be to separate whatever action is invoked from the barcode scanner (maybe put it into a project library script that accepts a string argument). Then you can invoke it from your barcode scanner automatically, or call it from a text field with a submit button (or whatever other hook you want).

1 Like

Right, that makes sense, but what if the action returns data that needs to be assigned to other components and trigger binding updates. Wouldn’t I have to duplicate the script to assign those props etc. on both the barcode scanner and the manual entry component? That isn’t that big of a deal, Just trying to minimize duplicate code.

Or am I missing a better way to handle that from a project script?

I would say to keep your project script as ‘pure’ as possible - limit side effects. Instead, return whatever data might be necessary, and at the call site(s), just invoke system.perspective.sendMessage with your payload and the appropriate scope. Then each component that ‘cares’ about this prop can have a message handler, and thus the relationships between things don’t have to be as tightly coupled.

3 Likes

Appreciate the insight! I’ll take a look into using this methodology.

1 Like

One of the biggest things I am trying to instill in my coworkers is writing your scripts in a project library first and then calling them from the appropriate spots. I have found that it really does simplify things and when the time comes when you realize you need to call some business logic in a second or third location, you are already set up to do that.

Also don’t underestimate how much easier and quicker it is to get nice overview of your data model or object in your system when all scripts relating to the object are in a single place. I have found it makes overall debugging much easier.

Most of my logic are in project scripts, but I have left component updates in the event scripts after the project script call. Switching that over to message handling should be the next thing I need to do to take it to the next level.

1 Like

And I totally agree with, not only debugging, but updating the logic is so much easier when only having to change it in one place!

1 Like

So is your suggestion to call the project script and supply session and page id to the function, then call perspective.sendMessage() from the project script to supply the return payload?

My personal take is that I would separate the two.
Have one project script that handles the core business logic/whatever requirement, and then a separate script (this could be a custom method on the view, if you wanted) that takes the payload from the first script and broadcasts it.
You could also do something a little bit ‘magic’ with Python involving decorators, e.g.:

import functools

def broadcast_return_value(handler, scope="page"):
	def broadcaster(func):
		@functools.wraps(func)
		def wrapper_decorator(*args, **kwargs):
			value = func(*args, **kwargs)
			system.perspective.sendMessage(handler, value, scope)
			return value
		return wrapper_decorator
	return broadcaster

You could ‘tag’ any function with this decorator, and it will automatically broadcast the return value to the specified handler. E.G:

@broadcast.broadcast_return_value("handler")
def abc(*args, **kwargs):
	return {
		'args': list(args),
		'kwargs': dict(kwargs)
	}

Then when I’m invoking test.abc, my return value is automatically broadcast with a message type of handler:

def runAction(self, event):
	test.abc("abc", "def", ghi="ghi")
# ---
def onMessageReceived(self, payload):
	system.perspective.print(payload)

And when I click the button:
13:21:36.609 [Browser Thread: 58bdde8f-b55e-45c1-89cd-193545bdf7b6] INFO Perspective.Designer.Workspace - {'args': ['abc', 'def'], 'kwargs': {'ghi': 'ghi'}}

This decorator approach doesn’t require you to pass in a session or page ID because they’re implicitly supplied - despite not being known where you’re authoring the script, they are known as long as you’re invoking the script from a Perspective component. :magic_wand:

3 Likes

Thanks! It will take a little bit for me to digest this :face_with_monocle:

I think Paul is worse than me for exotic code. :grin:

3 Likes

I will absolutely disclaim: I’m not the guy who has to maintain your HMI. I’m not gonna get any calls at 3AM if something isn’t working. Making code “functional” (limit side effects, give each function clear responsibilities) is a general principle that I think is always useful, but using an exotic language feature like decorators is definitely more situational. I just wanted to show off a fun possible approach.

4 Likes

I will admit to using decorators for one oddball client project. :man_shrugging: