The latest version of this document can be found at www.broad.ology.org.uk/amiga/proaction/05_A_Simple_App.html
1: #!python 2: # 3: ################################################################ 4: # 5: # Anatomy of a ProAction Application. 6: # 7: # 05_A_Simple_App.py 8: # 9: # Extended and customising our Application Class 10: # Using the getfile.gadget 11: # Combining the various elements into a nmore advance GUI 12: # 13: ################################################################ 14: # 15: # 16: 17: import sys 18: import os 19: import arexx 20: 21: class Application: 22: 23: def __init__(self): 24: 25: self.PORTNAME = "MakePDF" 26: self.UNIQUE = True 27: self.selected = "2" 28: self.command = "GHOSTSCRIPT:gs -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sPAPERSIZE=%s -dNOPAUSE -dBATCH -dQUIET "-sOutputFile=%s" "%s" " 29: 30: # Disable all our gadgets 31: def disable(self, guikey): 32: arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + self.cnlist_gid + " TAGSTRING "GA_Disabled,1,TAG_DONE"") 33: arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + self.convert_gid + " TAGSTRING "GA_Disabled,1,TAG_DONE"") 34: arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + self.getPSfile_gid + " TAGSTRING "GA_Disabled,1,TAG_DONE"") 35: arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + self.getPDFfile_gid + " TAGSTRING "GA_Disabled,1,TAG_DONE"") 36: 37: # Enable all our gadgets 38: def enable(self, guikey): 39: arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + self.cnlist_gid + " TAGSTRING "GA_Disabled,0,TAG_DONE"") 40: arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + self.convert_gid + " TAGSTRING "GA_Disabled,0,TAG_DONE"") 41: arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + self.getPSfile_gid + " TAGSTRING "GA_Disabled,0,TAG_DONE"") 42: arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + self.getPDFfile_gid + " TAGSTRING "GA_Disabled,0,TAG_DONE"") 43: 44: 45: # Parse the ghostscript statd script to determine 46: # available page sizes 47: 48: def GetPageSizeList(): 49: 50: statd_filename = "GHOSTSCRIPT:Data/8.54/lib/gs_statd.ps" 51: 52: statd_handle = open(statd_filename,'r') 53: 54: if statd_handle: 55: found_sizes = False 56: page_size_list = [] 57: for line in statd_handle: 58: if found_sizes: 59: if line[:10] == "%END SIZES": 60: break 61: line = line.strip() 62: if line[:1] != "/": 63: continue 64: parts = line.split() 65: page_size = parts[0].lstrip('/') 66: if parts[1][0] != "/": 67: page_size_list.append(page_size) 68: if line[:13] == "/.setpagesize": 69: found_sizes = True 70: statd_handle.close() 71: return page_size_list 72: 73: 74: # Certain gadgets such as Listbrowsers, Choosers, RadioButtons etc require 75: # lists of nodes of elements in the gadget to be built in advance 76: # ProAction provides functions to deal with these based on the Exec functions. 77: 78: def MakeChooserNodeList(guikey, *strings): 79: concstrings = [] 80: # First we need an exec list to hold our ChooserNodes 81: # We create this with a special command NEWGUIOBJECT and specify an object 82: # Of type GUIOBJ_List . An ID for the list object will be returned that 83: # We can use in all further operations on it. 84: 85: (rc,rc2,cnlist) = arexx.dorexx("PROACTION","NEWGUIOBJECT GUIID " + guikey + " OBJECTTYPE "GUIOBJ_List"") 86: # Before proceding verify the allocation above worked, else things may go badly 87: if rc == 0: 88: # Our function recives a tuple in the variable strings, which may consist of 89: # seperate strings or lists of strings, depening on how we call it. 90: # We need a (python) list of strings so this next assembles that for us. 91: 92: 93: for string in strings: 94: if not isinstance(string,list): 95: concstrings.append(string) 96: else: 97: concstrings.extend(string) 98: 99: # Now we process the list of strings we have 100: 101: for string in concstrings: 102: # Build atag list for a new node. 103: cntags = "" 104: cntags += "CNA_Text," + str(string) + "," 105: cntags += "TAG_DONE" 106: # The command ALLOCCHOOSERNODE is a Wrapper for the AlloChooserNode() C 107: # function. It returns an ID for a GUIOBJ_Node Object which we can add to the 108: # growing choosernode list. 109: 110: (rc,rc2,cn) = arexx.dorexx("PROACTION","ALLOCCHOOSERNODE GUIID " + guikey + " TAGSTRING "" + cntags + """) 111: if rc == 0: 112: # verify our success and add to the tail of the list with teh ADDTAIL command 113: arexx.dorexx("PROACTION", "ADDTAIL GUIID " + guikey + " LISTID " + cnlist + " NODEID " + cn ) 114: 115: return cnlist 116: 117: # Our choosernodes are not automatically released at script exit so we need to 118: # Clean up the list. The list it self will be freed on automatically on exit. 119: 120: def FreeChooserNodeList(guikey, rbnlist): 121: # We remove each node in turn using the REMTAIL command. 122: (rc, rc2, rbn) = arexx.dorexx("PROACTION","REMTAIL GUIID " + guikey + " LISTID " + rbnlist) 123: while rc == 0: 124: # if succesful we pass the NODEID to the FREECHOOSERNODE command. 125: arexx.dorexx("PROACTION","FREECHOOSERNODE GUIID " + guikey + " NODEID " + rbn) 126: # remove next node and loop. 127: (rc, rc2, rbn) = arexx.dorexx("PROACTION","REMTAIL GUIID " + guikey + " LISTID " + rbnlist) 128: 129: 130: # 131: # HandleInput() receives commands at the ARexx port and acts accordingly. 132: # They may be commands from ProAction (the CLOSE command in this example) 133: # Or they maybe commands from other ARexx based scripts etc. 134: # 135: 136: def HandleInput(pyport,guikey): 137: 138: global app 139: 140: # Loop until die becomes non zero 141: die = 0 142: while die == 0: 143: # Wait for a message to arrive at our Port 144: pyport.wait() 145: # Get the first message (but there may be more than one) 146: msg = pyport.getmsg() 147: # Loop until all queued messages are processed 148: while msg: 149: cmd = msg.msg 150: msg.reply() 151: if cmd == "QUIT": 152: die = 1 153: break 154: elif cmd[:5] == "CLOSE": 155: # Window Close Button Pressed 156: die = 1 157: break 158: elif cmd[:8] == "GADGETUP": 159: # The format of this command is 160: # GADGETUP GUIID guikey GADGETID gid CODE code 161: # We uses the split() method of the python string object to neatly parse this. 162: # The value lguikey would allow us to deal with multiple GUI windows. 163: (dummy,dummy,lguikey,dummy,gid,dummy,code) = cmd.split() 164: if gid == app.convert_gid: 165: pagesize = app.pagesizelist[int(app.selected)] 166: 167: # Use the GETATTR command to determine the chosen filenames 168: 169: (rc,rc2,PSfile) = arexx.dorexx("PROACTION","GETATTR GUIID " + guikey + " OBJECTID " + app.getPSfile_gid + " TAGNAME "GETFILE_File"") 170: (rc,rc2,PSdrawer) = arexx.dorexx("PROACTION","GETATTR GUIID " + guikey + " OBJECTID " + app.getPSfile_gid + " TAGNAME "GETFILE_Drawer"") 171: 172: # Use the os.path API to cobine the drawer and file names for maximum portabilty 173: # and ease. 174: PSpath = os.path.join(PSdrawer,PSfile) 175: 176: # Repeat for PDF destination 177: 178: (rc,rc2,PDFfile) = arexx.dorexx("PROACTION","GETATTR GUIID " + guikey + " OBJECTID " + app.getPDFfile_gid + " TAGNAME "GETFILE_File"") 179: (rc,rc2,PDFdrawer) = arexx.dorexx("PROACTION","GETATTR GUIID " + guikey + " OBJECTID " + app.getPDFfile_gid + " TAGNAME "GETFILE_Drawer"") 180: PDFpath = os.path.join(PDFdrawer,PDFfile) 181: 182: # This builds our command line from the template defined in the class definition above 183: 184: command = app.command % (pagesize,PDFpath, PSpath) 185: 186: # Disable the GUI whilst working 187: app.disable(guikey) 188: 189: # Call the external command. This could fail but it's failure won't interrup our script. 190: 191: os.system(command) 192: 193: # Enable the GUI. 194: 195: app.enable(guikey) 196: 197: elif gid == app.cnlist_gid: 198: app.selected = code 199: elif gid == app.getPSfile_gid: 200: (rc,rc2,chosen) = arexx.dorexx("PROACTION","REQUESTFILE GUIID " + guikey + " OBJECTID " + app.getPSfile_gid) 201: elif gid == app.getPDFfile_gid: 202: (rc,rc2,chosen) = arexx.dorexx("PROACTION","REQUESTFILE GUIID " + guikey + " OBJECTID " + app.getPDFfile_gid) 203: else: 204: print cmd 205: msg = pyport.getmsg() 206: 207: 208: def DoGUI(pubscreen): 209: 210: global app 211: 212: 213: # Our script needs an incoming ARexx port to receive messages from 214: # ProAction. We create this using the arexx modules Port method 215: # Which returns a Port object 216: 217: 218: 219: pyport = arexx.Port(app.PORTNAME) 220: if pyport: 221: if app.UNIQUE: 222: if pyport.name != app.PORTNAME: 223: ErrorExit("ARexx Port " + app.PORTNAME + " already Exists!") 224: 225: # First we'll build our window.class tagslist 226: # Python can't use send StemVars at the moment so we'll 227: # use the TAGSTRING tecnique throughout. 228: 229: wintags = "" 230: wintags += "WA_Width,300," 231: wintags += "WA_Height,100," 232: wintags += "WA_DragBar,1," 233: wintags += "WA_DepthGadget,1," 234: wintags += "WA_SizeGadget,1," 235: wintags += "WA_CloseGadget,1," 236: wintags += "WA_Title,"+ sys.argv[0] + "," 237: wintags += "WA_PubScreenFallBack,1," 238: wintags += "WA_PubScreenName," + pubscreen + "," 239: wintags += "WINDOW_Position,WPOS_CENTERSCREEN," 240: wintags += "WINDOW_LockHeight,1," 241: wintags += "WA_Activate,1," 242: wintags += "TAG_DONE" 243: 244: # Now a taglist to define our top level layout.gadget 245: 246: layouttags = "" 247: layouttags += "LAYOUT_Orientation,LAYOUT_ORIENT_VERT," 248: layouttags += "LAYOUT_HorizAlignment,LALIGN_CENTER," 249: layouttags += "TAG_DONE" 250: 251: # Tags for our label image. 252: labeltags = "" 253: labeltags += "LABEL_Justification,LJ_CENTER," 254: # Notice in this next line we pass the newline as "*N" this is because the 255: # ProAction commands are parsed with ReadArgs, so muxt be "DOS Encoded" 256: labeltags += "LABEL_Text,PDF Maker*NPlease choose source and destination file names," 257: labeltags += "TAG_DONE" 258: 259: # tags for two getfile gadgets, for the source and destination. 260: # NB the gadget is set to read only as we don't have the abilty to 261: # define a hook for it with ProAction at present. 262: # But we will activate the reuester via the REQUESTFILE command when the GUI is running 263: 264: getPSfiletags = "" 265: getPSfiletags += "GETFILE_TitleText,Select Postscript Source," 266: getPSfiletags += "GETFILE_DoSaveMode,0," 267: getPSfiletags += "GA_RelVerify,1," 268: getPSfiletags += "GETFILE_ReadOnly,1," 269: getPSfiletags += "GETFILE_DoMultiSelect,0," 270: getPSfiletags += "GETFILE_Drawer,RAM:," 271: getPSfiletags += "GETFILE_File,ww.ps," 272: getPSfiletags += "TAG_DONE" 273: 274: 275: getPDFfiletags = "" 276: getPDFfiletags += "GETFILE_TitleText,Select Destination," 277: getPDFfiletags += "GETFILE_DoSaveMode,1," 278: getPDFfiletags += "GA_RelVerify,1," 279: getPDFfiletags += "GETFILE_ReadOnly,1," 280: getPDFfiletags += "GETFILE_DoMultiSelect,0," 281: getPDFfiletags += "GETFILE_Drawer,RAM:," 282: getPDFfiletags += "GETFILE_File,poster.pdf," 283: getPDFfiletags += "TAG_DONE" 284: 285: 286: 287: (rc,rc2,guikey) = arexx.dorexx("PROACTION","CREATEGUI PORTNAME " + pyport.name + " TAGSTRING "" + wintags + """) 288: if rc == 0: 289: # we can now build our GUI here; 290: 291: # firstly we add the top level layout gadget, we use a special command 292: # ADDLAYOUT to do this, which in combination with ENDLAYOUT manages nesting 293: # of complex layout trees for us. 294: 295: (rc,rc2,current_layout_gid) = arexx.dorexx("PROACTION","ADDLAYOUT GUIID " + guikey + " TAGSTRING "" + layouttags + """) 296: 297: # Here we add a label image directly to the layout with the ADDIMAGE command 298: 299: 300: (rc,rc2,dummy) = arexx.dorexx("PROACTION","ADDIMAGE GUIID " + guikey + " IMAGECLASS "label.image" TAGSTRING "" + labeltags + """) 301: 302: app.pagesizelist = GetPageSizeList() 303: numlabels = len(app.pagesizelist) 304: app.cnlist = MakeChooserNodeList(guikey,app.pagesizelist) 305: 306: if (app.cnlist != None): 307: (rc,rc2,app.cnlist_gid) = arexx.dorexx("PROACTION","ADDGADGET GUIID " + guikey + " GADGETCLASS "chooser.gadget" TAGSTRING "CHOOSER_Labels," + app.cnlist + ",CHOOSER_MaxLabels," + str(numlabels) + ",CHOOSER_Selected," + app.selected + ",GA_RelVerify,1,GA_Disabled,0,TAG_DONE"") 308: 309: # This next two lines demonstrates how to add a label to gadget. 310: # The label must be applied to the CHILD object of our current_layout 311: # Using the LAYOUT_ModifyChild tag. 312: # It's only safe to use this tag before the GUI window is opened. 313: # Otherwise we must use the LMMODIFYCHILD command 314: 315: # NB when we create an image this way we must set the NODISPOSE option. 316: # The reason being is that the lautout gad wil dispose of the attached image 317: # Automatically. So we don't want ProAction to do it. (Double dispose == death...) 318: # By default images create with NEWIMAGE are disposed of when the ProAction 319: # object is destroyed. 320: 321: (rc,rc2,label_iid) = arexx.dorexx("PROACTION","NEWIMAGE GUIID " + guikey + " IMAGECLASS "label.image" NODISPOSE TAGSTRING "LABEL_Justification,LJ_RIGHT,LABEL_Text,Page size:,TAG_DONE"") 322: (rc,rc2,dummy) = arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + current_layout_gid + " TAGSTRING "LAYOUT_ModifyChild," + app.cnlist_gid + ",CHILD_Label," + label_iid + ",TAG_DONE"") 323: 324: 325: (rc,rc2,app.getPSfile_gid) = arexx.dorexx("PROACTION","ADDGADGET GUIID " + guikey + " GADGETCLASS "getfile.gadget" TAGSTRING "" + getPSfiletags + """) 326: (rc,rc2,label_iid) = arexx.dorexx("PROACTION","NEWIMAGE GUIID " + guikey + " IMAGECLASS "label.image" NODISPOSE TAGSTRING "LABEL_Justification,LJ_RIGHT,LABEL_Text,Postscript file to convert:,TAG_DONE"") 327: (rc,rc2,dummy) = arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + current_layout_gid + " TAGSTRING "LAYOUT_ModifyChild," + app.getPSfile_gid + ",CHILD_Label," + label_iid + ",TAG_DONE"") 328: 329: (rc,rc2,app.getPDFfile_gid) = arexx.dorexx("PROACTION","ADDGADGET GUIID " + guikey + " GADGETCLASS "getfile.gadget" TAGSTRING "" + getPDFfiletags + """) 330: (rc,rc2,label_iid) = arexx.dorexx("PROACTION","NEWIMAGE GUIID " + guikey + " IMAGECLASS "label.image" NODISPOSE TAGSTRING "LABEL_Justification,LJ_RIGHT,LABEL_Text,PDF file to output:,TAG_DONE"") 331: (rc,rc2,dummy) = arexx.dorexx("PROACTION","SETATTRS GUIID " + guikey + " OBJECTID " + current_layout_gid + " TAGSTRING "LAYOUT_ModifyChild," + app.getPDFfile_gid + ",CHILD_Label," + label_iid + ",TAG_DONE"") 332: 333: # Now we add a button gadget. The ADDGADGET function adds the 334: # gadget to the currently active layout. We save the resulting ID 335: # in our Application class object app. 336: 337: (rc,rc2,app.convert_gid) = arexx.dorexx("PROACTION","ADDGADGET GUIID " + guikey + " GADGETCLASS "button.gadget" TAGSTRING "GA_Text,_Convert To PDF,GA_RelVerify,1,TAG_DONE"") 338: 339: # End of top level layout. 340: (rc,rc2,current_layout_gid) = arexx.dorexx("PROACTION","ENDLAYOUT GUIID " + guikey ) 341: 342: # Open our Window. 343: 344: (rc,rc2,result) = arexx.dorexx("PROACTION","OPENGUIWINDOW GUIID " + guikey) 345: 346: # Handle events at the ARexx port. 347: HandleInput(pyport,guikey) 348: 349: # Now that we have a chhoser node we **Must** close the window before 350: # Releaseing the nodelist 351: 352: arexx.dorexx("PROACTION","CLOSEGUIWINDOW GUIID " + guikey) 353: 354: # Clean UP Our Nodes 355: FreeChooserNodeList(guikey, app.cnlist) 356: 357: # All done now shutdown the GUI 358: arexx.dorexx("PROACTION","DESTROYGUI GUIID " + guikey) 359: else: # pyport 360: ErrorExit("Couldn't create our ARexx port") 361: 362: # 363: # ErrorExit() throws up an RequestChoice notification to indicate 364: # a critical error. 365: # 366: 367: def ErrorExit(msg): 368: # Build our command as a string. 369: # We can use sys.argv[0] for the script name to make thsi more reusable. 370: command = "RequestChoice TYPE "ERROR" "" + sys.argv[0] + "" "" + msg + "" "OK" >NIL:" 371: # Call the command via the os.system() 372: os.system(command) 373: exit() 374: 375: # 376: # The support GetPorts() returns a list of public MsgPorts 377: # as a string 378: # 379: 380: 381: def GetPorts(): 382: # Depending on the version of python we may have a useful 383: # function that retuns just what we need, so we use the try 384: # except syntax to test. 385: 386: try: 387: ports = os.getports() 388: ports = " ".join(ports) 389: except AttributeError: 390: # The getports function wasn't present so we call the REXX 391: # equivalent. 392: (rc,rc2,ports) = arexx.dorexx("REXX","return show('P')") 393: if rc != 0: 394: # We couldn't even call REXX somwthing serioiusly up here 395: # Bail out with a requester. 396: ErrorExit("Couldn't Find ARexx!") 397: return ports 398: 399: 400: # Now we need to check that ProAction is present an available to us, 401: # So first get a list of active ports 402: 403: ports = GetPorts() 404: 405: # Then check for ProActions port which will be "PROACTION". 406: # Port names are case sensitive. 407: 408: if -1 == ports.find("PROACTION"): 409: # No ProAction start it 410: os.system("RUN >"T:proactionpid" *>NIL: APPDIR:PROACTION") 411: os.system("C:WaitForPort PROACTION") 412: # Now check again 413: ports = GetPorts() 414: if -1 == ports.find("PROACTION"): 415: # Still not there :-( 416: ErrorExit("Unable to start or find ProAction GUIServer") 417: 418: 419: # Okay we should be good to go now 420: # Initialise our aplication 421: 422: app = Application(); 423: 424: DoGUI("Workbench") 425: