The latest version of this document can be found at www.broad.ology.org.uk/amiga/proaction/04_Lists_And_Nodes.html
1: #!python 2: # 3: ################################################################ 4: # 5: # Anatomy of a ProAction Application. 6: # 7: # 04_Lists_And Nodes.py 8: # 9: # Creating a ChooserNodeList 10: # Adding to the GUI 11: # Handling Input 12: # Cleaning up on exit. 13: # 14: ################################################################ 15: # 16: # 17: 18: import sys 19: import os 20: import arexx 21: 22: class Application: 23: 24: def __init__(self): 25: 26: self.PORTNAME = "TestScript" 27: self.UNIQUE = True 28: self.selected = "1" 29: 30: # Certain gadgets such as Listbrowsers, Choosers, RadioButtons etc require 31: # lists of nodes of elements in the gadget to be built in advance 32: # ProAction provides functions to deal with these based on the Exec functions. 33: 34: def MakeChooserNodeList(guikey, *strings): 35: concstrings = [] 36: # First we need an exec list to hold our ChooserNodes 37: # We create this with a special command NEWGUIOBJECT and specify an object 38: # Of type GUIOBJ_List . An ID for the list object will be returned that 39: # We can use in all further operations on it. 40: 41: (rc,rc2,cnlist) = arexx.dorexx("PROACTION","NEWGUIOBJECT GUIID " + guikey + " OBJECTTYPE "GUIOBJ_List"") 42: # Before proceding verify the allocation above worked, else things may go badly 43: if rc == 0: 44: # Our function recives a tuple in the variable strings, which may consist of 45: # seperate strings or lists of strings, depening on how we call it. 46: # We need a (python) list of strings so this next assembles that for us. 47: 48: 49: for string in strings: 50: if not isinstance(string,list): 51: concstrings.append(string) 52: else: 53: concstrings.extend(string) 54: 55: # Now we process the list of strings we have 56: 57: for string in concstrings: 58: # Build atag list for a new node. 59: cntags = "" 60: cntags += "CNA_Text," + str(string) + "," 61: cntags += "TAG_DONE" 62: # The command ALLOCCHOOSERNODE is a Wrapper for the AlloChooserNode() C 63: # function. It returns an ID for a GUIOBJ_Node Object which we can add to the 64: # growing choosernode list. 65: 66: (rc,rc2,cn) = arexx.dorexx("PROACTION","ALLOCCHOOSERNODE GUIID " + guikey + " TAGSTRING "" + cntags + """) 67: if rc == 0: 68: # verify our success and add to the tail of the list with teh ADDTAIL command 69: arexx.dorexx("PROACTION", "ADDTAIL GUIID " + guikey + " LISTID " + cnlist + " NODEID " + cn ) 70: 71: return cnlist 72: 73: # Our choosernodes are not automatically released at script exit so we need to 74: # Clean up the list. The list it self will be freed on automatically on exit. 75: 76: def FreeChooserNodeList(guikey, rbnlist): 77: # We remove each node in turn using the REMTAIL command. 78: (rc, rc2, rbn) = arexx.dorexx("PROACTION","REMTAIL GUIID " + guikey + " LISTID " + rbnlist) 79: while rc == 0: 80: # if succesful we pass the NODEID to the FREECHOOSERNODE command. 81: arexx.dorexx("PROACTION","FREECHOOSERNODE GUIID " + guikey + " NODEID " + rbn) 82: # remove next node and loop. 83: (rc, rc2, rbn) = arexx.dorexx("PROACTION","REMTAIL GUIID " + guikey + " LISTID " + rbnlist) 84: 85: 86: # 87: # HandleInput() receives commands at the ARexx port and acts accordingly. 88: # They may be commands from ProAction (the CLOSE command in this example) 89: # Or they maybe commands from other ARexx based scripts etc. 90: # 91: 92: def HandleInput(pyport,guikey): 93: 94: global app 95: 96: # Loop until die becomes non zero 97: die = 0 98: while die == 0: 99: # Wait for a message to arrive at our Port 100: pyport.wait() 101: # Get the first message (but there may be more than one) 102: msg = pyport.getmsg() 103: # Loop until all queued messages are processed 104: while msg: 105: cmd = msg.msg 106: msg.reply() 107: if cmd == "QUIT": 108: die = 1 109: break 110: elif cmd[:5] == "CLOSE": 111: # Window Close Button Pressed 112: die = 1 113: break 114: elif cmd[:8] == "GADGETUP": 115: # The format of this command is 116: # GADGETUP GUIID guikey GADGETID gid CODE code 117: # We uses the split() method of the python string object to neatly parse this. 118: # The value lguikey would allow us to deal with multiple GUI windows. 119: (dummy,dummy,lguikey,dummy,gid,dummy,code) = cmd.split() 120: if gid == app.ok_gid: 121: die = 1 122: print "OK Pressed" 123: elif gid == app.cnlist_gid: 124: print code 125: else: 126: print cmd 127: msg = pyport.getmsg() 128: 129: 130: def DoGUI(pubscreen): 131: 132: global app 133: 134: 135: # Our script needs an incoming ARexx port to receive messages from 136: # ProAction. We create this using the arexx modules Port method 137: # Which returns a Port object 138: 139: 140: 141: pyport = arexx.Port(app.PORTNAME) 142: if pyport: 143: if app.UNIQUE: 144: if pyport.name != app.PORTNAME: 145: ErrorExit("ARexx Port " + app.PORTNAME + " already Exists!") 146: 147: # First we'll build our window.class tagslist 148: # Python can't use send StemVars at the moment so we'll 149: # use the TAGSTRING tecnique throughout. 150: 151: wintags = "" 152: wintags += "WA_Width,300," 153: wintags += "WA_Height,100," 154: wintags += "WA_DragBar,1," 155: wintags += "WA_DepthGadget,1," 156: wintags += "WA_SizeGadget,1," 157: wintags += "WA_CloseGadget,1," 158: wintags += "WA_Title,"+ sys.argv[0] + "," 159: wintags += "WA_PubScreenFallBack,1," 160: wintags += "WA_PubScreenName," + pubscreen + "," 161: wintags += "WINDOW_Position,WPOS_CENTERSCREEN," 162: wintags += "WA_Activate,1," 163: wintags += "TAG_DONE" 164: 165: # Now a taglist to define our top level layout.gadget 166: 167: layouttags = "" 168: layouttags += "LAYOUT_Orientation,LAYOUT_ORIENT_VERT," 169: layouttags += "LAYOUT_HorizAlignment,LALIGN_CENTER," 170: layouttags += "TAG_DONE" 171: 172: # Tags for our label image. 173: labeltags = "" 174: labeltags += "LABEL_Justification,LJ_CENTER," 175: # Notice in this next line we pass the newline as "*N" this is because the 176: # ProAction commands are parsed with ReadArgs, so muxt be "DOS Encoded" 177: labeltags += "LABEL_Text,A Label*NOn Two Lines," 178: labeltags += "TAG_DONE" 179: 180: 181: (rc,rc2,guikey) = arexx.dorexx("PROACTION","CREATEGUI PORTNAME " + pyport.name + " TAGSTRING "" + wintags + """) 182: if rc == 0: 183: # we can now build our GUI here; 184: 185: # firstly we add the top level layout gadget, we use a special command 186: # ADDLAYOUT to do this, which in combination with ENDLAYOUT manages nesting 187: # of complex layout trees for us. 188: 189: (rc,rc2,current_layout_gid) = arexx.dorexx("PROACTION","ADDLAYOUT GUIID " + guikey + " TAGSTRING "" + layouttags + """) 190: 191: # Here we add a label image directly to the layout with the ADDIMAGE command 192: 193: 194: (rc,rc2,dummy) = arexx.dorexx("PROACTION","ADDIMAGE GUIID " + guikey + " IMAGECLASS "label.image" TAGSTRING "" + labeltags + """) 195: 196: 197: app.cnlist = MakeChooserNodeList(guikey,"Testing","TESTING",["1","2","3"]) 198: 199: if (app.cnlist != None): 200: (rc,rc2,app.cnlist_gid) = arexx.dorexx("PROACTION","ADDGADGET GUIID " + guikey + " GADGETCLASS "chooser.gadget" TAGSTRING "CHOOSER_Labels," + app.cnlist + ",CHOOSER_MaxLabels," + str(6) + ",CHOOSER_Selected," + app.selected + ",GA_RelVerify,1,GA_Disabled,0,TAG_DONE"") 201: 202: 203: # Now we add a button gadget. The ADDGADGET function adds the 204: # gadget to the currently active layout. We save the resulting ID 205: # in our Application class object app. 206: 207: (rc,rc2,app.ok_gid) = arexx.dorexx("PROACTION","ADDGADGET GUIID " + guikey + " GADGETCLASS "button.gadget" TAGSTRING "GA_Text,_OK,GA_RelVerify,1,TAG_DONE"") 208: 209: # End of top level layout. 210: (rc,rc2,current_layout_gid) = arexx.dorexx("PROACTION","ENDLAYOUT GUIID " + guikey ) 211: 212: # Open our Window. 213: 214: (rc,rc2,result) = arexx.dorexx("PROACTION","OPENGUIWINDOW GUIID " + guikey) 215: 216: # Handle events at the ARexx port. 217: HandleInput(pyport,guikey) 218: 219: # Now that we have a chhoser node we **Must** close the window before 220: # Releaseing the nodelist 221: 222: arexx.dorexx("PROACTION","CLOSEGUIWINDOW GUIID " + guikey) 223: 224: # Clean UP Our Nodes 225: FreeChooserNodeList(guikey, app.cnlist) 226: 227: # All done now shutdown the GUI 228: arexx.dorexx("PROACTION","DESTROYGUI GUIID " + guikey) 229: else: # pyport 230: ErrorExit("Couldn't create our ARexx port") 231: 232: # 233: # ErrorExit() throws up an RequestChoice notification to indicate 234: # a critical error. 235: # 236: 237: def ErrorExit(msg): 238: # Build our command as a string. 239: # We can use sys.argv[0] for the script name to make thsi more reusable. 240: command = "RequestChoice TYPE "ERROR" "" + sys.argv[0] + "" "" + msg + "" "OK" >NIL:" 241: # Call the command via the os.system() 242: os.system(command) 243: exit() 244: 245: # 246: # The support GetPorts() returns a list of public MsgPorts 247: # as a string 248: # 249: 250: 251: def GetPorts(): 252: # Depending on the version of python we may have a useful 253: # function that retuns just what we need, so we use the try 254: # except syntax to test. 255: 256: try: 257: ports = os.getports() 258: ports = " ".join(ports) 259: except AttributeError: 260: # The getports function wasn't present so we call the REXX 261: # equivalent. 262: (rc,rc2,ports) = arexx.dorexx("REXX","return show('P')") 263: if rc != 0: 264: # We couldn't even call REXX somwthing serioiusly up here 265: # Bail out with a requester. 266: ErrorExit("Couldn't Find ARexx!") 267: return ports 268: 269: 270: # Now we need to check that ProAction is present an available to us, 271: # So first get a list of active ports 272: 273: ports = GetPorts() 274: 275: # Then check for ProActions port which will be "PROACTION". 276: # Port names are case sensitive. 277: 278: if -1 == ports.find("PROACTION"): 279: # No ProAction start it 280: os.system("RUN >"T:proactionpid" *>NIL: APPDIR:PROACTION") 281: os.system("C:WaitForPort PROACTION") 282: # Now check again 283: ports = GetPorts() 284: if -1 == ports.find("PROACTION"): 285: # Still not there :-( 286: ErrorExit("Unable to start or find ProAction GUIServer") 287: 288: 289: # Okay we should be good to go now 290: # Initialise our aplication 291: 292: app = Application(); 293: 294: DoGUI("Workbench") 295: