Code: spiro.ppl (general python version) |
Code: python program
or app (DM42 code for the same!) |
I work in a lab where laptops are pervasive. They pushed out calculators, which decades earlier did the same to slide rules. As calculators become more powerful, for some jobs the Prime could be more convenient than laptops and regain a spot on the workbench. We write python DSP code and while the Prime has the power to do some of that work, networking is needed to get signals onto it and results off it. Unfortunately, the Prime does not have Wifi, limiting greater use. But programmability keeps it attractive for small tasks and there are two options on the Prime: PPL (Prime Programming Language) and python.
PPL is well documented in the Prime user's manual and many web pages. Using information from the manual and some examples online, to learn PPL I wrote the small Spirograph-like program above that can be dropped into the HP Prime Programs folder visible from the HP Prime Connectivity Kit.
Python on the Prime is more challenging because in 2024 there is no formal HP python documentation. My first effort is the green hat above. You can use links below the picture to download either the program or the Prime App version. The App is dropped into Applications Library folder, and for some reason you must hit the 'Clear' soft button after starting the app.
Since official Prime documentation for python programming is yet to be written, I list a few things I've learned through googling and a good bit of trial and error. An important discovery is that the subroutine hpprime.eval('<PPL command>') is a way to get at PPL commands that don't have python equivalents. In particular, it allows easy access to menu-oriented subroutines so that you don't have to write your own. The paragraphs below all have the caveat that these are solutions I found. Please let me know if there are better ones.
Numpy (NUMeric PYthon) is a large library and unsurprisingly not part of micropython. Nevertheless, it is hard living without it. Your numpy vectors and matrices must be rewritten to use the Prime's linalg.matrix and will of course be constrained to what linalg routines exist.
#PYTHON PPLwrapper print('Super complicated python functions go here!')> #END EXPORT myFunc() BEGIN PYTHON(PPLwrapper); END;
The name you use rather than PPLwrapper is unimportant because the name that will show up in the program is myFunc. When you run the program, if there are multiple functions you will be prompted to choose which to execure.
User taps on the screen are categorized as mouse events in the Prime. The user manual has details on the MOUSE() call. I simply reuse the following subroutines in each of my programs requiring mouse input:
import hpprime as h def mouseClear(): # Clear out old mouse events like button bounces. while h.eval('mouse(1)')>=0: pass def mousePt(): # Wait forever for a screen touch. while True: h.eval('wait(0.1)') # Throttle i/o loop. f1,f2 = h.eval('mouse') # Touch info for fingers 1 and 2. if len(f1) > 0: # Got a finger touch! return f1,f2 # [x,y,xOrig,yOrig,type], [x,y,xOrig,yOrig,type]
mouseClear clears out any outstanding mouse events that we don't care about. If no events are waiting a -1 is returned and is why output of 0 or more is checked for. MOUSE(1) returns the x coordinate of a tap that would have been returned. mousePt is useful when your program must wait until there is a screen touch, for example when a soft menu choice is required. It returns data for both finger touches. The second finger data will be empty in the case of a single finger touch.
import hpprime as h c = h.eval('X:=0;choose(X,"Title","Choice 1","Choice 2","Choice 3")')
The first parameter to CHOOSE() must be defined before use and is any capital letter. The return value begins with 1 (not 0).
The full code sequence for a program's main loop might be:
import hpprime as h def main(): while True: h.eval('drawmenu("Item0", "Item1", "Item2", "Item3", "Item4", "Item5")') m = mousePt() # Get user screen tap coordinates. b = softPick(m) # -1 if not a soft menu tap. if b == 0: doItem0() elif b == 1: doItem1() elif b == 2: doItem2() elif b == 3: doItem3() elif b == 4: doItem4() elif b == 5: doItem5()
Determining which soft button is pushed is easy keeping in mind that the six soft buttons are 53x20 pixels and the screen is 320x240 pixels.
def softPick(pt): # pt is [x, y, xOrig, yOrig, type] return -1 if pt[1]<220 else pt[0]//53 # Soft button is 53x20 pixels.
Line breaks are used for clarity. The first argument to input() is a variable list. These same variables are put in a list after the input() call completes to move values from the PPL call environment to python. Any valid variable name can be used in python. In this example, values of PPL D and F are assigned to d_m and f_Hz, which are used in subsequent python code. The second input() arg is pop up menu title and the third arg is the list of labels shown by each input box. The last arg of input() is more detailed help messages shown at the bottom of the Prime's screen when each input box is highlighted.
Even relatively simple screen layouts quickly require somewhat complicated code. The screen shown here:
is created with this python code snippet. Notice the use of variable res to determine if the user canceled or not:
Details of INPUT() usage can be looked up in the manual's PPL command summaries. Note, however, the manual says that in the variables section the matrix [-1] can be used to indicate that the edit field accepts all data types. It results in an error for me, however, and I must use integers as given in the PPL TYPE() output list. I find myself most often using [0], indicating that only reals are accepted.
import hpprime as h cmd = 'msgbox("%s")' % s h.eval(cmd)
There are at least two ways to move program results to the calculator environment for later use.
The steps shown in the picture above are:
Importantly, from the Prime Home and CAS environments the value is retrieved by typing myProg.myVar or using the same from another program. The hard work of this program can be utilized externally.
Creating an app is only a very little bit more involved than writing a program, but is easiest to devote the following section to it.
Next, add python files and an icon. The icon is what shows up on the Prime App screen and seems to be 38x38 pixels. To add files and icon start up the Connectivity Kit and turn on your calculator. Under Applications you'll see your new app and right click on its name:
For each python and other needed files you must click 'Add file' and navigate to it. And same for your custom icon after clicking 'Add app icon'. After your code has been added to the app you simply choose it from the App icon list and then the soft menu 'Start' option. No doubt it will run bug free first try because you are an expert coder! :-) Here is a demo program you can add to an app:
import hpprime as h def main(): h.eval('AVars("MyVar"):=1234') h.eval('AVars("MyVar2"):=(3,4)') h.eval('AVars("MyVar3"):="Mike"') n=h.eval('AVars("MyVar")') c=h.eval('AVars("MyVar2")') s=h.eval('AVars("MyVar3")') h.eval('print') # Clear screen. print('n=1234 is now type %s' % str(type(n))) print('c=(3,4) is now type %s' % str(type(c))) print('s="Mike" is now type %s' % str(type(s))) print('Press + to end demo.') while True: h.eval('wait(0.2)') # Throttle i/o loop. # Retrieve mouse & keyboard events. b=h.eval('getkey') # Keyboard button press, -1 if none since last call. f1,f2=h.eval('mouse') # Finger 1, finger 2 touch data. if len(f1)>0: # Finger touch sensed. # Mouse data: [x,y,xOrig,yOrig,type]. print('You touched (%d,%d)' % (f1[0],f1[1]), end='') if len(f2)>0: # Two finger touch. print(' and (%d,%d)' % (f2[0],f2[1])) else: print() if b==-1: # No button pressed. continue print('You pressed button %d' % b) if b==50: # + button exits demo. print('Slán!') break main()
Finally, you must create the app's main program. I find it easiest to simply import my code and not otherwise put code here:
The first roughly ten lines of main() show how you can share app results externally. This is necessary if you want to work with program results outside the program. I save an int, complex and string. The first three lines of output show what is retrieved
and we see that all is well, though int becomes float. After the program runs, switch to CAS where you can retrieve and work with the results of your program:
The infinite loop in the program above shows how to handle button pushes and screen touches. Screen touch data is returned for two fingers. You can try this demo code on your real Prime. Depending on your computer the two finger events might or might not register. The WAIT(0.2) ensures that I/O is only done 5 times a second. Next, both mouse and keyboard events are retrieved. If keyboard button number is -1 then no button was pushed since last check. If MOUSE returns [[],[]] then no screen touch happened since last check. The User Manual describes how GETKEY works and provides a useful diagram showing button numbering. For this demo code the "+" button stops the demo.
AVars() is used share results from an app for use outside the program. However, there is a parsing bug in PPL when in the HOME environment. If there is a plus sign in the exponent, a valid construct, it results in an error. For example, 2e1 is parsed properly but 2e+1 incorrectly yields an error message. This is an issue because python generates numbers that way when the '%e' format is used. An excellent solution shared by Piotr Kowalewski uses cas.caseval():
import cas def numToAvars(varName, val): cmd = 'AVars("%s"):=%s' % (varName, str(val)) cas.caseval(cmd)
To move a number out of the app to a variable named 'solution', your python program calls numToAvars:
numToAvars('solution', myNumber)
I made an RF Calcs app that uses UI techniques learned, including placement of UI items, button debouncing and similar little things. It might be useful to see a working example.
The preceding has been gleaned from trial and error, emailing, User Manual's section on PPL and a good bit of general web searching. I'm certain that there are better ways of doing some of the things I presented. If you can improve the above, please get in touch!
Thank you to Piotr Kowalewski and Eddie Shore for their help. I appreciate them sharing their expertise and helping me learn how to use my calculator. I'm the operator with my pocket calculator. :-)
Many thanks,
Mike Markowski, mike.ab3ap@gmail.com