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: