CHAPTER 13 User Interfaces

7KH3ULQFLSOHRI/HDVW$VWRQLVKPHQW0DNHDXVHU LQWHUIDFHDVFRQVLVWHQWDQGDVSUHGLFWDEOHDVSRVVLEOH ¢$QRQ\PRXV

8QOHVVDQDSSOLFDWLRQLVGHHSO\HPEHGGHGRUVSHFLILFDOO\GHVLJQHGWRUXQDVDEDFN JURXQGSURFHVVLWZLOOSUREDEO\QHHGVRPHW\SHRIXVHULQWHUIDFH,QWKLVFKDSWHUZH ZLOOH[SORUHGLIIHUHQWZD\VWRFRPPXQLFDWHZLWKDXVHU:H¦OOVWDUWE\H[DPLQLQJZKDW \RXFDQGRZLWKMXVWWKHFRPPDQGOLQH1H[WZH¦OOORRNDWKRZWRXVHDQ$16,FDSDEOH WHUPLQDOHPXODWRUSURJUDPWRGLVSOD\GDWDDQGDFFHSWLQSXWDQGWKHQWKHcursesVFUHHQ FRQWUROSDFNDJHIRU3\WKRQ$IWHUWKLVZH¦OOPRYHWRWKHUHDOPRIEULJKWFRORUVIDQF\ JUDSKVLPDJHVDQGGLDORJVZLWKDORRNDWWKH7N,QWHU*8,WRRONLWSURYLGHGZLWK WKHVWDQGDUG3\WKRQGLVWULEXWLRQ:H¦OODOVRWDNHDTXLFNWRXURIWKHZ[3\WKRQ*8, SDFNDJH Text-Based Interfaces 7H[WEDVHGLQWHUIDFHVDUHWKHIRXQGDWLRQRIDOOGLVSOD\EDVHGFRPSXWHUXVHULQWHUIDFHV ,VD\GLVSOD\EDVHGEHFDXVHWHFKQLFDOO\WKHILUVWLQWHUIDFHVZHUHSDQHOVIXOORIOLJKWVDQG VZLWFKHV3ULQWLQJWHUPLQDOVFDPHQH[WEXWLWZDVQ¦WXQWLOWKHDGYHQWRI&57WHUPLQDOV WKDWKXPDQPDFKLQHLQWHUIDFH +0, GHYLFHVEHJDQWRFRPHLQWRWKHLURZQ

The Console 7KHPRVWVWUDLJKWIRUZDUGZD\WRLQWHUDFWZLWKDQ\SURJUDP3\WKRQRURWKHUZLVHLV WKURXJKWKHFRQVROHLQWHUIDFH8QGHU:LQGRZVWKLVLVWKHVRFDOOHG£'26ER[¤RUWKH £FPG SURPSW¤ DV LW¦V FXUUHQWO\ NQRZQ WKH :LQGRZV VKHOO DSSOLFDWLRQ LV FDOOHG FPGH[H 2QD8QL[RU/LQX[V\VWHPLWLVWKHVKHOOSURPSWIRUVKEDVKNVKWVFKRU ZKDWHYHUHOVH\RXPD\KDSSHQWREHXVLQJIRUDVKHOOLQWHUIDFH,IQRZLQGRZPDQDJHU LVDFWLYHWKHZKROHVFUHHQLVWKHFRQVROHEXWLWEHKDYHVMXVWOLNHWKHZLQGRZHGIRUP

487 6HQGLQJGDWDWRWKHVKHOOLQWKHIRUPRIVWULQJVLVHDV\¢WKDW¦VZKDWWKHprintVWDWHPHQW GRHV*HWWLQJLQSXWEDFNIURPDXVHULVQRWDVLQWXLWLYHO\REYLRXVPDLQO\EHFDXVH3\ WKRQGRHVQRWKDYHFURVVSODWIRUPHTXLYDOHQWVRIgetch()RUgetche() JHWFKDUDFWHU DQGJHWFKDUDFWHUZLWKHFKRUHVSHFWLYHO\ ,WGRHVKRZHYHUKDYHWKHraw_input()EXLOW LQPHWKRGZKLFKFDQKDQGOHPRVWRIWKHFRPPRQXVHULQSXWFKRUHVDQGLWZRUNVRQ DOOWKHSODWIRUPVWKDW3\WKRQVXSSRUWV6KRUWO\,¦OOVKRZ\RXKRZWRXVHFW\SHVDQGWKH &UXQWLPHOLEUDU\IRUERWK/LQX[DQG:LQGRZVEXWILUVWOHW¦VVHHKRZPXFKZHFDQGR ZLWKMXVWWKHFRQVROHLQWHUIDFH

Example console display :KLOH\RXFRXOGDOZD\VMXVWXVHRFFDVLRQDOprintVWDWHPHQWVWRRXWSXWGDWDWRWKH FRQVROHLWPLJKWEHGLIILFXOWIRUDXVHUWRWHOOZKDW¦VKDSSHQLQJDVWKHGDWDVFUROOVE\ RQWKHVFUHHQ(VWDEOLVKLQJDWHPSODWHIRUWKHGDWDGLVSOD\DQGXVLQJDIL[HGIRUPDW PDNHVWKHGDWDHDVLHUWRUHDGDQGJUHDWO\UHGXFHVXVHUIUXVWUDWLRQ DQGWKDWXVHUPLJKW EH\RX  ,I\RXZDQWWRXVHDIL[HGIRUPDWIRUGDWDRXWSXWWRWKHFRQVROH\RX¦OOQHHGDWOHDVWWZR WKLQJVVRPHZD\WRFOHDUWKHVFUHHQDQGDWHPSODWHWRFRQWDLQ\RXUGDWD:H¦OOORRN DWWKHWHPSODWHILUVW 8QOHVVZHUHVRUWWR$16,WHUPLQDOFRQWUROVWULQJV GLVFXVVHGLQWKHQH[WVHFWLRQ D SURJUDPGRHVQ¦WKDYHDQ\ZD\WRSRVLWLRQWKHFXUVRULQWKHFRQVROHZLQGRZ(DFKOLQH LQWKHGLVSOD\QHHGVWRWDNHFDUHRILWVRZQSRVLWLRQLQJDQGGDWDGLVSOD\IRUPDWWLQJ 7KHOLVWLQJIRUconsole1.pyVKRZVKRZWKLVLVGRQH #! /usr/bin/python # console1.py # # Demonstrates console output using the built-in print method. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

import time

ainstat = ['OFF','OFF','OFF','OFF'] aindata = [0.0, 0.0, 0.0, 0.0] discstate = ['OFF','OFF','OFF','OFF','OFF','OFF','OFF','OFF'] discin = [0,0,0,0,0,0,0,0]

print '\n' * 50 print "" print "System Status" print "" print "Analog Input 0 : %10s %f" % (ainstat[0], aindata[0]) print "Analog Input 1 : %10s %f" % (ainstat[1], aindata[1]) print "Analog Input 2 : %10s %f" % (ainstat[2], aindata[2]) print "Analog Input 3 : %10s %f" % (ainstat[3], aindata[3]) print "" print "Discrete Input 0: %3s %d" % (discstate[0], discin[0])

488 | Chapter 13:ಗUser Interfaces print "Discrete Input 1: %3s %d" % (discstate[1], discin[1]) print "Discrete Input 2: %3s %d" % (discstate[2], discin[2]) print "Discrete Input 3: %3s %d" % (discstate[3], discin[3]) print "Discrete Input 4: %3s %d" % (discstate[4], discin[4]) print "Discrete Input 5: %3s %d" % (discstate[5], discin[5]) print "Discrete Input 6: %3s %d" % (discstate[6], discin[6]) print "Discrete Input 7: %3s %d" % (discstate[7], discin[7]) print ""

time.sleep(2) 9HUWLFDOSRVLWLRQLQJLVEDVHGRQWKHRUGHULQZKLFKWKHOLQHVDUHSULQWHGRXWWRWKH FRQVROHDQGKRUL]RQWDOSRVLWLRQLQJLVGHWHUPLQHGE\KRZHDFKOLQHLVIRUPDWWHG7KH OLQHRIFRGHLPPHGLDWHO\IROORZLQJWKHIRXUYDULDEOHLQLWLDOL]DWLRQOLQHVprint "\n" * 50VLPXODWHVD£FOHDUVFUHHQ¤E\JHQHUDWLQJEODQNOLQHV,XVHGEHFDXVH,KDYH ODUJHFRPPDQGOLQHZLQGRZVEXWJHQHUDWLQJPRUHEODQNOLQHVWKDQWKHZLQGRZVXS SRUWVGRHVQRWKXUWDQ\WKLQJ7KHUHVXOWORRNVOLNHWKLV System Status

Analog Input 0 : ACTIVE 0.000000 Analog Input 1 : OFF 0.000000 Analog Input 2 : ACTIVE 0.000000 Analog Input 3 : OFF 0.000000

Discrete Input 0: OFF 0 Discrete Input 1: OFF 1 Discrete Input 2: OFF 0 Discrete Input 3: OFF 0 Discrete Input 4: OFF 0 Discrete Input 5: OFF 0 Discrete Input 6: OFF 0 Discrete Input 7: OFF 0 6LQFHWKHUDZFRQVROH,2GRHVQ¦WKDYHDJHQHULFFOHDUVFUHHQIXQFWLRQDVHULHVRISULQW VWDWHPHQWVZLWKHPSW\VWULQJVFDQEHXVHGWRVFUROORIIDQ\WKLQJFXUUHQWO\RQWKHGLVSOD\ LQRUGHUWRPDNHURRPIRUQHZGDWD1RWHWKDWLIWKHZLQGRZLVODUJHUWKDQWKHQXPEHU RIOLQHVLQWKHWHPSODWHWKHWHPSODWHWH[WZLOOHQGXSDWWKHERWWRPRIWKHZLQGRZ WKH ODVWOLQHSULQWHGZLOOEHMXVWDERYHWKHSURPSW  +RZHYHUWKHUHLVDQRWKHUZD\:HFDQILQGRXWZKDWW\SHRI26WKHFRGHLVUXQQLQJ XQGHUDQGWKHQXVHWKHDSSURSULDWHPHWKRGWRLVVXHDVFUHHQFOHDUFRPPDQGOLNHWKLV def clrDisp(numlines=50): if os.name == "posix": os.system('') elif os.name in ("nt", "dos", "ce"): os.system('CLS') else: print '\n' * numlines +HUHLVDQH[DPSOHRIWKLVWHFKQLTXH #! /usr/bin/python # console2.py

Text-Based Interfaces | 489 # # Demonstrates console output using the built-in print method and # an OS-specific screen clear technique. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

import time import os

ainstat = ['OFF','OFF','OFF','OFF'] aindata = [0.0, 0.0, 0.0, 0.0] discstate = ['OFF','OFF','OFF','OFF','OFF','OFF','OFF','OFF'] discin = [0,0,0,0,0,0,0,0]

def clrDisp(numlines=50): if os.name == "posix": os.system('clear') elif os.name in ("nt", "dos", "ce"): os.system('CLS') else: print '\n' * numlines

def drawData(): print "" print "System Status" print "" print "Analog Input 0 : %10s %f" % (ainstat[0], aindata[0]) print "Analog Input 1 : %10s %f" % (ainstat[1], aindata[1]) print "Analog Input 2 : %10s %f" % (ainstat[2], aindata[2]) print "Analog Input 3 : %10s %f" % (ainstat[3], aindata[3]) print "" print "Discrete Input 0: %3s %d" % (discstate[0], discin[0]) print "Discrete Input 1: %3s %d" % (discstate[1], discin[1]) print "Discrete Input 2: %3s %d" % (discstate[2], discin[2]) print "Discrete Input 3: %3s %d" % (discstate[3], discin[3]) print "Discrete Input 4: %3s %d" % (discstate[4], discin[4]) print "Discrete Input 5: %3s %d" % (discstate[5], discin[5]) print "Discrete Input 6: %3s %d" % (discstate[6], discin[6]) print "Discrete Input 7: %3s %d" % (discstate[7], discin[7]) print ""

ainstat[1] = "ACTIVE" ainstat[3] = "ACTIVE" aindata[1] = 5.2 aindata[3] = 8.9 discstate[0] = "ON" discin[0] = 1 clrDisp() drawData() time.sleep(1.0)

ainstat[1] = "OFF" ainstat[3] = "OFF" aindata[1] = 0.0

490 | Chapter 13:ಗUser Interfaces aindata[3] = 0.0 ainstat[0] = "ACTIVE" ainstat[2] = "ACTIVE" aindata[0] = 5.2 aindata[2] = 8.9 discstate[0] = "OFF" discin[0] = 0 discstate[1] = "ON" discin[1] = 1 clrDisp() drawData() time.sleep(1.0) 1RZZHKDYHWKHIRXQGDWLRQIRUDXVDEOHWH[WEDVHGGLVSOD\7RXSGDWHWKHGLVSOD\ WKHVFUHHQLVILUVWFOHDUHGXVLQJWKHDSSURSULDWHFRPPDQGDQGWKHQWKHOLQHVIURPWKH WHPSODWHDUHZULWWHQXVLQJWKHprintPHWKRG7KLVUHVXOWVLQTXLFNHUVFUHHQUHJHQHUDWLRQ WKDQWKHPXOWLSOHOLQHVPHWKRGDQGLWORRNVQLFHUWRR,IWKH26FDQQRWEHGHWHUPLQHG WKHIXQFWLRQGHIDXOWVWRWKHEODQNOLQHPHWKRGWRFOHDUWKHGLVSOD\)LJXUHVKRZV WKHIORZFKDUWIRUDWH[WEDVHGFRQVROHGDWDGLVSOD\EDVHGRQZKDWZH¦YHVHHQVRIDU

Reading user input 1RZZHKDYHDPHDQVWRGLVSOD\GDWDEXWWKHUH¦VVWLOORQHWKLQJPLVVLQJXVHULQSXW )RUWXQDWHO\3\WKRQGRHVKDYHDRQHVL]HILWVDOOLQSXWPHWKRGWRUHDGLQSXWIURPWKH FRQVROH7KHraw_input()PHWKRGUHDGVDOLQHRILQSXWIURPWKHFRQVROHDQGUHWXUQVLW DVDVWULQJ$OLQHLQWKLVFDVHLVGHILQHGDV]HURRUPRUHFKDUDFWHUVWHUPLQDWHGZLWKDQ (2/7KH(2/ZLOOQRWEHDSSHQGHGWRWKHVWULQJUHWXUQHGE\raw_input() 7KHraw_input()PHWKRGFDQDOVRGLVSOD\DSURPSWVWULQJDQGLIWKHreadlinePRGXOH LVLQVWDOOHGLWZLOOXVHWKDWWRVXSSRUWLQSXWOLQHHGLWLQJDQGSURYLGHLQSXWKLVWRU\+HUH LVWKHWKLUGLWHUDWLRQRIWKHFRQVROHH[DPSOHZLWKraw_input()LQFRUSRUDWHGLQWRWKH FRGH #! /usr/bin/python # console3.py # # Demonstrates console output using the built-in print method and the # raw_input() method. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

import time import os

ainstat = ['OFF','OFF','OFF','OFF'] aindata = [0.0, 0.0, 0.0, 0.0] discstate = ['OFF','OFF','OFF','OFF','OFF','OFF','OFF','OFF'] discin = [0,0,0,0,0,0,0,0] x = 0

def initData():

Text-Based Interfaces | 491 global ainstat, aindata, discstate, discin

ainstat = ['OFF','OFF','OFF','OFF'] aindata = [0.0, 0.0, 0.0, 0.0] discstate = ['OFF','OFF','OFF','OFF','OFF','OFF','OFF','OFF'] discin = [0,0,0,0,0,0,0,0]

def readData(): global ainstat, aindata, discstate, discin, x

initData() discstate[x] = 'ON' discin[x] = 1 x += 1 if x > (len(discin) - 1): x = 0

def clrDisp(numlines=50): if os.name == "posix": os.system('clear') elif os.name in ("nt", "dos", "ce"): os.system('CLS') else: print '\n' * numlines

def drawData(): print "" print "System Status" print "" print "Analog Input 0 : %10s %f" % (ainstat[0], aindata[0]) print "Analog Input 1 : %10s %f" % (ainstat[1], aindata[1]) print "Analog Input 2 : %10s %f" % (ainstat[2], aindata[2]) print "Analog Input 3 : %10s %f" % (ainstat[3], aindata[3]) print "" print "Discrete Input 0: %3s %d" % (discstate[0], discin[0]) print "Discrete Input 1: %3s %d" % (discstate[1], discin[1]) print "Discrete Input 2: %3s %d" % (discstate[2], discin[2]) print "Discrete Input 3: %3s %d" % (discstate[3], discin[3]) print "Discrete Input 4: %3s %d" % (discstate[4], discin[4]) print "Discrete Input 5: %3s %d" % (discstate[5], discin[5]) print "Discrete Input 6: %3s %d" % (discstate[6], discin[6]) print "Discrete Input 7: %3s %d" % (discstate[7], discin[7]) print ""

while True: readData() clrDisp() drawData() instr = raw_input("Command: ") if instr.upper() == 'X': break console3.pyZLOOVWHSWKURXJKWKHGLVFUHWHLQSXWOLQHVRQHDWDWLPHHDFKWLPHWKH(QWHU NH\LVSUHVVHG,IDQx RUX FKDUDFWHULVHQWHUHGLWZLOOWHUPLQDWH

492 | Chapter 13:ಗUser Interfaces )LJXUH7H[WXDOGDWDGLVSOD\

7KHUHLVDGRZQVLGHKHUHKRZHYHU7KHraw_input()PHWKRGRQO\ZRUNVLQDEORFNLQJ PRGH,QRWKHUZRUGVLWZLOOEORFNXQWLOWKHXVHUHQWHUVVRPHWKLQJ7KLVPHDQVWKDW WKH GLVSOD\ FDQQRW DXWRPDWLFDOO\ XSGDWH LWVHOI EDVHG RQ D WLPHU VR ORQJ DV WKH raw_input()LVLQWKHPDLQGLVSOD\ORRS,WZRQ¦WIHWFKQHZGDWDDQGXSGDWHWKHGLVSOD\ XQWLOWKHXVHUSUHVVHVDQ\NH\RWKHUWKDQX ,WWXUQVRXWWKDWWKHUHLVDVROXWLRQWKRXJKWKLVLVDSHUIHFWSODFHIRUDWKUHDG:HFDQ OHWDWKUHDGIHWFKWKHLQSXWIURPWKHXVHUDQGDIODJFDQEHXVHGWRQRWLI\WKHPDLQORRS WKDWLW¦VWLPHWRWHUPLQDWH7KHFRGHIRUWKLVLVVKRZQKHUHDVconsole4.py #! /usr/bin/python # console4.py # # Demonstrates console output using the built-in print method and the # raw_input() method in a separate thread. #

Text-Based Interfaces | 493 # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

import time import os import threading

ainstat = ['OFF','OFF','OFF','OFF'] aindata = [0.0, 0.0, 0.0, 0.0] discstate = ['OFF','OFF','OFF','OFF','OFF','OFF','OFF','OFF'] discin = [0,0,0,0,0,0,0,0]

running = True x = 0

def initData(): global ainstat, aindata, discstate, discin

ainstat = ['OFF','OFF','OFF','OFF'] aindata = [0.0, 0.0, 0.0, 0.0] discstate = ['OFF','OFF','OFF','OFF','OFF','OFF','OFF','OFF'] discin = [0,0,0,0,0,0,0,0]

def readData(): global ainstat, aindata, discstate, discin, x

initData() discstate[x] = 'ON' discin[x] = 1 x += 1 if x > (len(discin) - 1): x = 0

def clrDisp(numlines=50): if os.name == "posix": os.system('clear') elif os.name in ("nt", "dos", "ce"): os.system('CLS') else: print '\n' * numlines

def drawData(): print "" print "System Status" print "" print "Analog Input 0 : %10s %f" % (ainstat[0], aindata[0]) print "Analog Input 1 : %10s %f" % (ainstat[1], aindata[1]) print "Analog Input 2 : %10s %f" % (ainstat[2], aindata[2]) print "Analog Input 3 : %10s %f" % (ainstat[3], aindata[3]) print "" print "Discrete Input 0: %3s %d" % (discstate[0], discin[0]) print "Discrete Input 1: %3s %d" % (discstate[1], discin[1]) print "Discrete Input 2: %3s %d" % (discstate[2], discin[2]) print "Discrete Input 3: %3s %d" % (discstate[3], discin[3]) print "Discrete Input 4: %3s %d" % (discstate[4], discin[4])

494 | Chapter 13:ಗUser Interfaces print "Discrete Input 5: %3s %d" % (discstate[5], discin[5]) print "Discrete Input 6: %3s %d" % (discstate[6], discin[6]) print "Discrete Input 7: %3s %d" % (discstate[7], discin[7]) print "" print "Enter X to terminate"

def getCommand(): global running

while True: instr = raw_input() if instr.upper() == 'X': running = False break

#------# launch the raw_input() handler thread getinput = threading.Thread(target=getCommand) getinput.start()

while running: readData() clrDisp() drawData() time.sleep(1.0) 1RZZHUHDOO\GRKDYHDJHQHULFIUDPHZRUNWKDWFDQEHXVHGWRFUHDWHDFRQVROHGDWD GLVSOD\WKDWFDQDOVRDFFHSWEDVLFFRPPDQGVDQGLWGRHVLWDOOXVLQJQRWKLQJPRUHWKDQ WKHprintDQGraw_input()EXLOWLQPHWKRGV -XVWLQFDVH\RXPLJKWEHWKLQNLQJWKDWDFRQVROHEDVHGWH[WRQO\GLVSOD\LVQ¦WSDUWLF XODUO\XVHIXO)LJXUHVKRZVDVFUHHQVKRWRIDVHWRIWH[WGLVSOD\ZLQGRZVXVHGWR WHVWVRPHRIWKHLQVWUXPHQWVXVHGIRUDVSDFHPLVVLRQ (DFKRIWKHVL[ZLQGRZVXVHVRQO\$6&,,WH[W7KHUH¦VQR$16,SRVLWLRQLQJRUDQ\WKLQJ HOVHIDQF\JRLQJRQKHUH ZHZLOOJHWWR$16,GLVSOD\FRQWUROVKRUWO\ 7KHGLVSOD\V VKRZWKHFRPPDQGLQWHUIDFHWRWKHVSDFHFUDIW¦V&38WKHUHVSRQVHVVHQWEDFNYLDWKH WHOHPHWU\FKDQQHOVDQGWKHVWDWXVRIWKHWHOHPHWU\SURFHVVLQJIXQFWLRQV7KHUH¦VHYHQ DZLQGRZIRUFRPPDQGLQSXWVDOWKRXJKWKLVV\VWHPZDVW\SLFDOO\GULYHQZLWKDQRWKHU WRRO QRWVKRZQ WKDWVHQWVHTXHQFHVRIFRPPDQGVLQWKHIRUPDWH[SHFWHGE\WKHIOLJKW VRIWZDUH

OS-specific console I/O 2QHRIWKHDGYDQWDJHV SHUKDSVWKHELJJHVWDGYDQWDJH RIXVLQJWH[WEDVHGGLVSOD\VOLNH WKRVHZH¦YHMXVWVHHQLVWKDWWKH\DUHODUJHO\SODWIRUPLQGHSHQGHQW7KH printDQG raw_input()PHWKRGVDUHQRWSODWIRUPVHQVLWLYHDQGRQO\WKHVFUHHQFOHDUPHWKRGV UHTXLUHVRPHNQRZOHGJHRIWKHXQGHUO\LQJ267KHUHDUHOLPLWDWLRQVKRZHYHUWRZKDW \RXFDQGRZLWKMXVWprintDQGraw_input()DQGVLWXDWLRQVPD\DULVHZKHUH\RXQHHG WRKDYHPRUHFRQWURORYHUWKHGLVSOD\DQGWKH,2ZLWKWKHXVHU

Text-Based Interfaces | 495 )LJXUH7H[WEDVHGWHVWV\VWHPGDWDGLVSOD\V %RWK/LQX[DQG:LQGRZVSURYLGHDEDVLFVHWRIIXQFWLRQVIRUGHDOLQJZLWKORZOHYHO FRQVROH,2:LWK/LQX[FRQVROH,2XWLOLW\IXQFWLRQVVXFKDVgetch()DQGungetch() DUHIRXQGLQWKHcursesOLEUDU\WKH\DUHQRWSDUWRIWKHVWDQGDUG,2IXQFWLRQVOLEUDU\ :LQGRZVSURYLGHVWKHVHDQGRWKHUIXQFWLRQVDVSDUWRIWKHmsvcrt'//PRGXOH3\WKRQ LQFOXGHVWKHOLEUDU\PRGXOHmsvcrtIRUDFFHVVLQJIXQFWLRQVLQWKH0LFURVRIWPVYFUW'// DQGWKHcursesOLEUDU\PRGXOHIRU8QL[/LQX[HQYLURQPHQWV,QWKHQH[WVHFWLRQZH¦OO VHHKRZWRSXWWKHVHDQGRWKHUORZOHYHOFRQVROH,2IXQFWLRQVWRZRUNWRFUHDWHVWUXF WXUHGWH[WEDVHGLQWHUIDFHVXVLQJWKH$16,WHUPLQDOFRQWUROVHTXHQFHVDQGWKHcurses VFUHHQGLVSOD\SDFNDJH%XWEHIRUHZHGHOYHLQWR$16,DQGcursesOHW¦VORRNDWKRZ WKHVHEDVLFIXQFWLRQVFDQEHXVHGZLWKWH[WEDVHGGLVSOD\V

Linux and Windows text display differences :HKDYHQRZUHDFKHGWKHSRLQWZKHUH/LQX[DQG:LQGRZVVWDUWWRJRWKHLUVHSDUDWH ZD\V2QFHZHOHDYHWKHGRPDLQRIVLPSOHprintVWDWHPHQWVDQGraw_input()ZHEHJLQ WRHQFRXQWHUVRPHVLJQLILFDQWGLIIHUHQFHVDULVLQJIURPWKHXQGHUO\LQJSKLORVRSKLHVRI WKHVHWZRRSHUDWLQJV\VWHPV 7KHDELOLW\WRFRQWURODGLVSOD\XVLQJ$16,FRQWUROVHTXHQFHVKDVORQJEHHQDSDUWRI 8QL[DQGODWHU/LQX[8QL[HYROYHGLQDPXOWLWDVNLQJWLPHVKDULQJHQYLURQPHQWEDVHG

496 | Chapter 13:ಗUser Interfaces RQPLQLFRPSXWHUVDQGPDLQIUDPHVZLWKWHUPLQDOVIRUXVHULQWHUIDFHVDQG/LQX[KDV LQKHULWHGWKDWSDUDGLJP:LQGRZVRQWKHRWKHUKDQGHYROYHGIURP'26ZLWKQR PXOWLWDVNLQJVXSSRUWDQGDVLQJOHVFUHHQIRURQHXVHU,WZDVQ¦WXQWLO0LFURVRIWUHOHDVHG WKHANSI.sysGULYHUPRGXOHWKDW3&VFRXOGLQWHUSUHW$16,VHTXHQFHVWRFRQWUROFXUVRU SRVLWLRQLQJDQGGLUHFWO\PDQLSXODWHDWH[WEDVHGGLVSOD\1RZDGD\V:LQGRZVVXSSRUW IRU$16,VFUHHQFRQWUROVLVDOPRVWQRQH[LVWHQW:LWKLWVHPSKDVLVRQD*8,IRUXVHU LQWHUDFWLRQ:LQGRZVGRHVQ¦WDSSHDUWRKDYHPXFKQHHGIRUWKHROGZD\RIGRLQJ WKLQJV +RZHYHUWKHODFNRIPXOWLWDVNLQJDQGWKUHDGV DWOHDVWLQLWLDOO\ZLWK'26 IRUFHGVRPH LQQRYDWLRQDQGDVDUHVXOWRQHXVHIXOIXQFWLRQWKDWDSSHDUVLQ'26DQG:LQGRZV HQYLURQPHQWVLVkbhit(),W¦VVRXVHIXOLQIDFWWKDWLW¦VDVWDQGDUGSDUWRIWKH0LFURVRIW 9LVXDO&5XQ7LPHOLEUDU\ WKHmsvcrt'//PRGXOH DQGKDVEHHQIRUDORQJWLPH 7KHUHLVQRGLUHFWHTXLYDOHQWLQ/LQX[ kbhit()LVDQRQEORFNLQJIXQFWLRQWKDWZLOOUHWXUQTrueLIWKHUHLVDFKDUDFWHUZDLWLQJLQ WKHFRQVROHLQSXWEXIIHU7KLVDOORZVDSURJUDPUXQQLQJLQDVLQJOHWDVNQRQWKUHDGHG HQYLURQPHQWWRFKHFNIRULQSXWLQDPDLQORRSZLWKRXWEORFNLQJWKHORRS UHFDOOWKH EORFNLQJEHKDYLRURIWKHconsole3.pyH[DPSOHIURPHDUOLHU ,QWKHconsole4.pyH[DP SOHDWKUHDGLVXVHGWRDFKLHYHIXQFWLRQDOLW\VRPHWKLQJOLNHWKDWRIkbhit()7KHUHLV RIFRXUVHDQRWKHUZD\WRGRWKLVXQGHU/LQX[WKDWLQYROYHVWKHXVHRIWKHselect() IXQFWLRQEXW,ZRQ¦WJHWLQWRWKDWKHUH,GRKRZHYHUHQFRXUDJH\RXWRH[SORUHLWRQ \RXURZQLW¦VDQLQWHUHVWLQJMRXUQH\ 8QGHU :LQGRZV WKH getche() IXQFWLRQ GRHV HVVHQWLDOO\ ZKDW 3\WKRQ¦V raw_input() PHWKRGGRHVZKHQJHWWLQJDVLQJOHFKDUDFWHUIURPWKHXVHU7RUHDGDVWULQJRIFKDU DFWHUVRQHDSSURDFKLVWRSXWgetche()LQVLGHDORRSWKDWH[HFXWHVXQWLODQ(2)LV GHWHFWHG7KLVZRUNVEXWLW¦VQRWYHU\HOHJDQW3\WKRQ¦Vraw_input()PHWKRGLVFRQ VLGHUDEO\PRUHSRZHUIXODQGLW¦VHDVLHUWRXVH

Using Python’s msvcrt library module )RU:LQGRZVHQYLURQPHQWV3\WKRQLQFOXGHVDKDQG\PRGXOHWKDWDOORZVDSURJUDP WRDFFHVVVRPHRIWKHORZOHYHOIXQFWLRQDOLW\SURYLGHGE\WKH0LFURVRIWUXQWLPHOLEUDU\ msvcrt1RWHWKDWWKLVGRHVQRWDSSO\WR/LQX[SODWIRUPV:HZLOOVHHZKDWFDQEHGRQH ZLWK/LQX[LQWKHQH[WVHFWLRQZKHQZHORRNLQWRcursesDQG$16,FRQWUROVHTXHQFHV 7DEOHOLVWVWKHIXQFWLRQVDYDLODEOHWKURXJK3\WKRQ¦VmsvcrtOLEUDU\PRGXOH

7DEOH3\WKRQ¦VPVYFUWPRGXOHIXQFWLRQV msvcrt function Description kbhit() Returns True if a keypress occurred and a character is waiting to be read from the input buffer. getch() Blocking call; waits for a keypress and then returns the character in the input buffer. Does not echo the input character to the console, and does not wait for an EOL before returning. getwch() The “wide char” version of getch(); returns a Unicode value.

Text-Based Interfaces | 497 msvcrt function Description getche() Identical to getch() except that the incoming character will be echoed to the console if it is a printable character. getwche() The “wide char” version of getche(); returns a Unicode value. putch(char) Prints a character directly to the console without buffering. putwch( The “wide char” version of putch() that accepts a Unicode character value for output. unicode_char) ungetch(char) Performs a “push-back” of the given character into the console buffer so that it will be the next character read by getch() or getche(). ungetwch( The “wide char” version of ungetch() that accepts a Unicode character value to push back. unicode_char)

7KHOLVWLQJIRUH[DPSOHSURJUDPconsole5.pyVKRZQQH[WLOOXVWUDWHVKRZkbhit()FDQ EHXVHGWRUHSODFHWKHWKUHDGDQGraw_input()WHFKQLTXHZHVDZLQconsole4.py #! /usr/bin/python # console5.py # # Demonstrates console output using the built-in print method and the # use of the kbhit() function from msvcrt. # # Windows only! Will not work under Linux. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

import time import os import msvcrt

ainstat = ['OFF','OFF','OFF','OFF'] aindata = [0.0, 0.0, 0.0, 0.0] discstate = ['OFF','OFF','OFF','OFF','OFF','OFF','OFF','OFF'] discin = [0,0,0,0,0,0,0,0]

running = True x = 0

def initData(): global ainstat, aindata, discstate, discin

ainstat = ['OFF','OFF','OFF','OFF'] aindata = [0.0, 0.0, 0.0, 0.0] discstate = ['OFF','OFF','OFF','OFF','OFF','OFF','OFF','OFF'] discin = [0,0,0,0,0,0,0,0]

def readData(): global ainstat, aindata, discstate, discin, x

initData() discstate[x] = 'ON'

498 | Chapter 13:ಗUser Interfaces discin[x] = 1 x += 1 if x > (len(discin) - 1): x = 0

def clrDisp(numlines=50): if os.name == "posix": os.system('clear') elif os.name in ("nt", "dos", "ce"): os.system('CLS') else: print '\n' * numlines

def drawData(): print "" print "System Status" print "" print "Analog Input 0 : %10s %f" % (ainstat[0], aindata[0]) print "Analog Input 1 : %10s %f" % (ainstat[1], aindata[1]) print "Analog Input 2 : %10s %f" % (ainstat[2], aindata[2]) print "Analog Input 3 : %10s %f" % (ainstat[3], aindata[3]) print "" print "Discrete Input 0: %3s %d" % (discstate[0], discin[0]) print "Discrete Input 1: %3s %d" % (discstate[1], discin[1]) print "Discrete Input 2: %3s %d" % (discstate[2], discin[2]) print "Discrete Input 3: %3s %d" % (discstate[3], discin[3]) print "Discrete Input 4: %3s %d" % (discstate[4], discin[4]) print "Discrete Input 5: %3s %d" % (discstate[5], discin[5]) print "Discrete Input 6: %3s %d" % (discstate[6], discin[6]) print "Discrete Input 7: %3s %d" % (discstate[7], discin[7]) print "" print "Enter X to terminate"

def getCommand(): global running

if msvcrt.kbhit(): inchar = msvcrt.getch() if inchar.upper() == 'X': running = False

#------while running: readData() clrDisp() drawData() getCommand() if running == True: time.sleep(1.0) 2QHPDMRUGLIIHUHQFHEHWZHHQ console4.pyDQG console5.pyLVLQKRZWKHIXQFWLRQ getCommand()LVLPSOHPHQWHG,Qconsole5.pyLW¦VQRORQJHUDWKUHDGDQGraw_input() KDVEHHQUHSODFHGZLWKkbhit()-XVWHQWHULQJDQXLVQRZVXIILFLHQWLQVWHDGRIXIROORZHG E\ WKH (QWHU NH\ DV LQ console4.py +RZHYHU WKH SULPDU\ GLIIHUHQFH EHWZHHQ

Text-Based Interfaces | 499 console4.pyDQGconsole5.pyLVWKDWconsole5.pyZLOOQRWUXQXQGHU/LQX[HYHQWKRXJK WKH EHKDYLRU RI ERWK LV YLUWXDOO\ LGHQWLFDO 8VLQJ WKH msvcrt PRGXOH KDV UHQGHUHG console5.py26VSHFLILFDQGFRQILQHGLWWRWKH:LQGRZVHQYLURQPHQW

ANSI Display Control Techniques 7KHPLGVVDZWKHDGYHQWRIWKH9LGHR'LVSOD\7HUPLQDO 9'7 DVDVXFFHVVRUWR WKHNH\SXQFKDQGWHOHW\SHPDFKLQHVWKDWZHUHSUHYDOHQWDWWKHWLPH7KHVXFFHVVRIWKH 9'7ZDVGXHSDUWO\WRKRZLWZDVLQWHUIDFHGZLWKWKHFRPSXWHUV\VWHPDQGSDUWO\WR LWVDELOLW\WRVXSSRUWIOH[LEOHDQGG\QDPLFGLVSOD\V$VLPSOHVHULDOLQWHUIDFH W\SLFDOO\ 56 ZDVDOOWKDWZDVQHHGHGWRFRQQHFWDWHUPLQDOWRWKHPDLQIUDPHDQGZLWKWKH DSSURSULDWHWLPHVKDULQJ26WKHV\VWHPFRXOGQRZVXSSRUWPXOWLSOHXVHUVDWWKHVDPH WLPH7KLVZDVSRVVLEOHEHFDXVHWKHFKRUHVRIVFUHHQPDQDJHPHQWDQGNH\ERDUGLQSXW ZHUHRIIORDGHGRQWRWKHWHUPLQDOVRDOOWKHPDLQIUDPHKDGWRGRZDVVHQGGDWDDQG FRPPDQGVWRWKHWHUPLQDODQGJHWWKHLQSXWIURPWKHXVHUZKHQLWEHFDPHDYDLODEOH ,QDVHQVHLWZDVDQHDUO\DSSOLFDWLRQRIGLVWULEXWHGSURFHVVLQJ 7KLQJVHYROYHGTXLWHUDSLGO\DQGLWVRRQEHFDPHDSSDUHQWWKDWLWZRXOGEHQLFHWRKDYH WKHDELOLW\WRSRVLWLRQWKHFXUVRUDQ\ZKHUHRQWKHVFUHHQWRVXSSRUWGDWDHQWU\LQWR SUHIRUPDWWHGGLVSOD\WHPSODWHVRUIRUPV7KHDELOLW\WRFRQWUROWKHFXUVRUSRVLWLRQZDV DOVRQHFHVVDU\WRLPSOHPHQWIXOOVFUHHQWH[WHGLWRUVVXFKDVYL 8QL[ DQGHGW XVHGRQ '(&¦V90626 ,QFLGHQWDOO\SHRSOHDOVRGLVFRYHUHGWKDWVFUHHQFRQWUROZDVXVHIXO IRUJDPHVDQGLI\RX¦YHHYHUSOD\HGZLWK5RJXH1HWKDFNRU(PSLUH\RX¦YHVHHQWKH GLUHFWGHVFHQGDQWVRIWKRVHHDUO\JDPHV6RPHRIWKHPZHUHTXLWHLPSUHVVLYHJLYHQ WKDWWKH\XVHGRQO\WKH$6&,,FKDUDFWHUVHW ,QLWLDOO\WHUPLQDOPDQXIDFWXUHUVZHQWDERXWGHYLVLQJWKHLURZQXQLTXHVFKHPHVIRU GRLQJWKHVFUHHQFRQWURODVPDQXIDFWXUHUVDUHZRQWWRGR7KLVTXLFNO\EHFDPHD SUREOHPEHFDXVHLI\RXZDQWHGWRDGGPRUHWHUPLQDOVWRDV\VWHPWKHUHZDVDULVN WKDWWKHQHZWHUPLQDOVZRXOGQ¦WZRUNZLWKWKHH[LVWLQJVRIWZDUH,QPRVWVFKHPHVWKH LGHDLVWRVHQGDQRQSULQWDEOHFKDUDFWHU RUVHWRIFKDUDFWHUV WRDOHUWWKHORJLFLQWKH WHUPLQDOWRUHFHLYHDVHTXHQFHRIFKDUDFWHUVIRUGLVSOD\FRQWURORUWRDOWHUWKHEHKDYLRU RIWKHWHUPLQDOLWVHOI6RORQJDVWHUPLQDOEUDQG;UHFRJQL]HVWKHVDPHVHTXHQFHVDV WHUPLQDOEUDQG<DOOLVZHOO%XWWKDWZDVQ¦WDOZD\VWKHFDVH8QL[V\VWHPVGHDOWZLWK WKLVE\LPSOHPHQWLQJ£WHUPFDS¤WKHWHUPLQDOFDSDELOLWLHVWUDQVODWLRQVFKHPHZKLFK FRXOGVHOHFWWKHDSSURSULDWHFRQWUROVHTXHQFHIRUDJLYHQIXQFWLRQEDVHGRQWKHWHUPLQDO PRGHO2Q/LQX[V\VWHPV\RXFDQVWLOOILQGWKHWHUPFDSGDWDEDVHILOHLQWKHHWFGLUHFWRU\ DOWKRXJKLWKDVVLQFHEHHQVXSHUVHGHGE\WHUPLQIR $Q LQLWLDO VWDQGDUG IRU $16, HVFDSH VHTXHQFHV FDPH LQWR H[LVWHQFH LQ  DV (&0$,WODWHUHYROYHGLQWR,62,(&7KH$16,LGHQWLILFDWLRQZDVDGRSWHG DURXQGZKHQLWZDVIRUPDOL]HGDV$16,;DQGWKLVLVVWLOOWKHPRVWFRPPRQ ZD\WRUHIHUWRWKHVWDQGDUGL]HGWHUPLQDOFRQWUROVHTXHQFHV7KURXJKRXWWKHUHVWRI WKLVFKDSWHUZKHQ,XVH£$16,¤\RXFDQDVVXPHWKDW,¦PUHIHUULQJWR(&0$$16, ;

500 | Chapter 13:ಗUser Interfaces (YHQWKRXJK9'7VDUHQRZODUJHO\H[WLQFWWKH$16,FRQWUROVHTXHQFHVOLYHRQDQG SUREDEO\ZLOOIRUDVORQJDVWKHUHDUHWH[WXDOGLVSOD\VRIRQHVRUWRUDQRWKHU)RUH[ DPSOHWKH;WHUPXWLOLW\RQ/LQX[ DQG8QL[ V\VWHPVVXSSRUWVWKH'(&979'7 VHWRIFRQWUROVHTXHQFHVZKLFKLVDUJXDEO\WKHPRVWFRPPRQLPSOHPHQWDWLRQRIWKH $16,VWDQGDUG;WHUPFDQDOVRVXSSRUWFRORUDQGHYHQKDVD7HNWURQL[HPXODWLRQ PRGH IRU VLPXODWHG  YHFWRU JUDSKLFV 6HH WKH 85/ LQ £6XJJHVWHG 5HDG LQJ¤RQSDJHIRUPRUHLQIRUPDWLRQDERXW;WHUP¦V$16,FDSDELOLWLHV

ANSI and Windows :LQGRZVLWVHOIGRHVQRWGLUHFWO\VXSSRUW$16,FRQVROHVFUHHQFRQWURODQG,GRQRW UHFRPPHQGWKHXVHRIWKHELW$16,V\VGULYHU$QRSHQVRXUFHUHSODFHPHQWIRU $16,V\VLV-DVRQ+RRG¦VDQVLFRQSDFNDJHZKLFKFRQWDLQV$16,GULYHUVIRUERWK ELW DQG ELW :LQGRZV $IWHU LQVWDOODWLRQ RI HLWKHU $16,GOO RU $16,GOO WKH FPGH[HZLQGRZZLOOEHDEOHWRKDQGOHDXVHIXOVXEVHWRIWKH$16,FRQWUROVHTXHQFHV EXWLWZRQ¦WZRUNZLWKWKHIXOOVHWRI$16,VHTXHQFHV

Basic ANSI control sequences /HW¦VVHHZKDWWKH$16,FRQWUROVHTXHQFHVORRNOLNH7DEOHLVDSDUWLDOOLVWLQJRI WKH$16,FRQWUROVHTXHQFHVVHWFRQVLVWLQJRIZKDW,FRQVLGHUWKHPRVWLPPHGLDWHO\ XVHIXOVHTXHQFHVIRURXUSXUSRVHVQDPHO\WKHFXUVRUSRVLWLRQLQJDQGVRPHGLVSOD\ PDQDJHPHQWVHTXHQFHV,¦YHOHIWRXWWKLQJVOLNHWKHGLVSOD\DWWULEXWHVFRQWUROV EULJKW GLPFRORUDQGVRRQ EHFDXVHDWWKLVSRLQWWKH\RQO\DGGFRPSOH[LW\WRDVXEMHFW,¦G OLNHWRNHHSVLPSOH+RZHYHULI\RXUHDOO\QHHGEULJKWEOXHEOLQNLQJFKDUDFWHUVRQDQ RUDQJHILHOGE\DOOPHDQVIHHOIUHHWRXVHWKHP 1RWHWKDWUHSUHVHQWVWKH$6&,,£HVFDSH¤FKDUDFWHUZKLFKFDQEHZULWWHQDVD 3\WKRQVWULQJLQWKHIRUP\x1b9DULDEOHQDPHVLQLWDOLFVUHSUHVHQWPRGLILDEOHGHFLPDO SDUDPHWHUVHJrowZRXOGEHUHSODFHGE\DURZQXPEHU)RUVRPHRIWKHVHTXHQFHV WZRFRPPDQGIRUPVDUHVKRZQ7KHVHFRQGIRUPLVWKHGHIDXOWIRUPZKHUH\RXFDQ RPLWWKHQXPHULFSDUDPHWHUVLIWKHGHIDXOWEHKDYLRULVDFFHSWDEOH)LQDOO\VRPHRIWKH VHTXHQFHVKDYHMXVWDQFKDUDFWHUDWWKHVWDUWZKLOHRWKHUVXVHDWZRFKDUDFWHU VHTXHQFH FRQVLVWLQJ RI [ WKDW¦V DQ HVFDSH FKDUDFWHU \x1b IROORZHG E\ D OHIW EUDFNHW[ 

Text-Based Interfaces | 501 7DEOH%DVLF$16,FRQWUROVHTXHQFHV Sequence Function Description [row ; col H Cursor Move or Home Sets the cursor position where subsequent text will begin. If no row/column parameters are provided (i.e., [H), the cursor will move to the home [H position at the upper-left corner of the screen. [count A Cursor Up Moves the cursor up by count rows; the default count is 1. [A [count B Cursor Down Moves the cursor down by count rows; the default count is 1. [B [count C Cursor Forward Moves the cursor forward by count columns; the default count is 1. [C [count D Cursor Backward Moves the cursor backward by count columns; the default count is 1. [D E Next Line Moves to start of next line. [s Save Cursor Saves current cursor position. [u Unsave Cursor Restores cursor position after a Save Cursor. D Move Down (Index) Moves the cursor down one line in the same column. M Move Up (Rev Index) Moves the cursor up one line in the same column. [0K Erase to End of Line Erases from the current cursor position to the end of the current line. [2K Erase Line Erases the entire current line. [2J Erase Screen Erases the screen with the background color and moves the cursor to home.

7KHQH[WH[DPSOHbgansi.pyLVDQH[DPSOHRIDEDUJUDSK¡W\SHGLVSOD\XVLQJWKHFRQ WUROVHTXHQFHVIURP7DEOH #!/usr/bin/python # bgansi.py # # Demonstrates basic ANSI screen control. Also demonstrates how to use # stdout from the sys library module. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

import random import time from sys import stdout

MAXEXT = 30 ROWSTRT = 7 # first row at 8th screen row COLSTRT = 9 # column start offset VALPOS = 45 # column for value

ran = random.random

502 | Chapter 13:ಗUser Interfaces output = stdout.write outflush = stdout.flush def generateBars(): # clear the screen output("\x1b[2J")

# put eight ID strings and markers on the screen in the leftmost # column starting at the 8th row from the top (row 7) i = 1 for row in range(ROWSTRT,15): # set cursor position output("\x1b[%d;%dH" % (row, 0)) # write marker character output("Chan %d |" % i) i += 1

# bars are numbered 0 through 7 def updateBar(barnum, extent): # adjust to match actual position of bar row = barnum + ROWSTRT

# limit extent to keep from hitting right edge of display if extent > MAXEXT: extent = MAXEXT # make sure something always gets printed if extent < 1: extent = 1

# clear the line first (lets graph line shrink) output("\x1b[%d;%dH" % (row, COLSTRT)) # erase to end of line output("\x1b[0K")

# walk through all positions up to extent for col in range(0, extent): # set position output("\x1b[%d;%dH" % (row, COLSTRT+col)) # use an equals sign to fill the bar output("=") # write the actual value used output("\x1b[%d;%dH" % (row, VALPOS)) output("%d" % extent) outflush() def runTest(): generateBars()

for x in range(0, 100): for barnum in range(0, 8): # the random number function returns a float value # between 0 and 1 use it to scale MAXEXT val = int(ran() * MAXEXT)

Text-Based Interfaces | 503 updateBar(barnum, val) # sleep briefly time.sleep(0.1)

output("\x1b[%d;%dH" % (20, 0)) outflush() print ""

if __name__ == '__main__': runTest() 7KHRXWSXWRIbgansi.pyLVVKRZQLQ)LJXUH7KLVLVMXVWDVLQJOHVQDSVKRWRIWKH VFUHHQZKHQUXQQLQJLWLVUDWKHUDFWLYH

)LJXUH$16,EDUJUDSKH[DPSOHRXWSXW

7KHUHDUHDIHZWKLQJVJRLQJRQLQbgansi.pyWKDWDUHZRUWKORRNLQJDWPRUHFORVHO\ )RUVWDUWHUVQRWLFHWKDW,¦PQRWXVLQJ3\WKRQ¦VEXLOWLQprintIXQFWLRQ,QWKLVH[DPSOH ZHMXVWZDQWWKHRXWSXWWRJRGLUHFWO\WRVWGRXWZLWKRXWDQ\LQWHUSUHWDWLRQRUPRGLIL FDWLRQVRWKHH[DPSOHGLUHFWO\LQYRNHVWKHwrite()PHWKRGRIWKHstdoutREMHFW$OVR VLQFHVWGRXWLVEXIIHUHGLQWHUQDOO\WKHflush()PHWKRGLVXVHGWRPDNHVXUHDOOWKHRXWSXW UHDOO\GRHVPDNHLWRXWZKHQLQWHQGHG 2QHVLJQLILFDQWWKLQJWRQRWLFHLQbgansi.pyLVWKDWWKH\x1b[VHTXHQFHNHHSVVKRZLQJ XSDQGWKDWWKH$16,FRQWUROVWULQJVWKDWXVHLWDUHQRWYHU\LQWXLWLYH:ULWLQJFRGH WKDWXVHVWKH$16,FRQWUROFRGHVLQWKLVIDVKLRQGHILQLWHO\TXDOLILHVDVGRLQJWKLQJVWKH KDUGZD\2QHFRXOGKLGHDORWRIWKLVE\XVLQJSVHXGRPDFURDVVLJQPHQWVOLNHWKLV CSI = "\x1b[" CLR = "2J"

504 | Chapter 13:ಗUser Interfaces %XWWKDW¦VQRWUHDOO\WKHEHVWVROXWLRQ$EHWWHUVROXWLRQLVDOLEUDU\RIPHWKRGVWKDWGR PRUHWKDQMXVWVZHHSWKHGHWDLOVXQGHUWKHUXJ,QWKHQH[WVHFWLRQZH¦OOVHHVXFKD OLEUDU\ZKLFKQRWRQO\HOLPLQDWHVWKHQHHGWRORRNXSWKHVHTXHQFHVEXWDOVRKDQGOHV ,2UHGLUHFWLRQ$IWHUWKDWZHZLOOH[DPLQH3\WKRQ¦VLQWHUIDFHWRWKHcursesOLEUDU\

The SimpleANSI library 1RWDOODSSOLFDWLRQVQHHGDOOWKHFDSDELOLWLHVRIWKHFRPSOHWH$16,FRQWUROVHTXHQFH VHW6RPHWLPHVMXVWEHLQJDEOHWRPRYHWKHFXUVRUDURXQGDQGDFFHSWXVHULQSXWIURP VSHFLILFORFDWLRQVLVVXIILFLHQW%XWWUXWKEHWROGWKHVHTXHQFHVGRWHQGWREHVRPHZKDW FOXPV\WRZRUNZLWKDQGFU\SWLFWRUHDGVRKDYLQJDQLFHFOHDQZUDSSHU$3,RIVRPH W\SHFDQKHOSTXLWHDELW /HW¦VDVVXPHWKDWZHKDYHDVFUHHQOD\RXWWKDWORRNVOLNH)LJXUH

)LJXUH$16,GDWDGLVSOD\

1RZOHW¦VWDNHORRNDWSimpleANSIDOLEUDU\PRGXOHWKDWSURYLGHVEDVLFVFUHHQFRQWURO IXQFWLRQVYLDWKHANSITermFODVVDQGVHHKRZLWFDQEHXVHGZLWK)LJXUH7KH ANSITermFODVVVXSSRUWVWKHFRQWUROVHTXHQFHVOLVWHGLQ7DEOHDQGLQFOXGHVDFRXSOH RIPRGLILFDWLRQVWRDOORZIRURSWLRQDOFRQWURORIWKHLQLWLDOFXUVRUSRVLWLRQIRUWKH(UDVH WR(QGRI/LQHDQG(UDVH/LQHVHTXHQFHV WKHANSITermPHWKRGVclrEOL()DQGclrLine() UHVSHFWLYHO\  7KHANSITermFODVVSURYLGHVSXEOLFPHWKRGVIRU$16,VFUHHQFRQWUROOLVWHGLQ7D EOH,WDOVRKDQGOHVVRFNHWVHULDODQGFRQVROH,2PRGHV

Text-Based Interfaces | 505 7DEOH$16,7HUPFODVVPHWKRGV Method Description clrEOL(row, col) Clears line from position at row, col to EOL. clrLine(row) Clears entire line (row) in display. clrScreen() Clears entire screen. indexDown() Moves/scrolls down one line. indexUp() Moves/scrolls up one line. moveBack(count) Moves cursor left count columns. moveDown(count) Moves cursor down count rows. moveForward(count) Moves cursor right count columns. moveHome() Moves cursor to upper-left corner. moveNextline() Moves to start of next line. movePos(row, col) Moves cursor to screen location row, col. moveUp(count) Moves cursor up count rows. readInput(reset=False) Gets user input and echoes it to the display. reset=True causes the cursor to return to the initial starting position when input is complete. resetDev() Resets terminal to initial state. restorePos() Restores cursor to last saved position. savePos() Saves current cursor position. writeOutput(outstr) Writes the specified string at the current cursor position.

7KHVRXUFHOLVWLQJIRUWKHSimpleANSIPRGXOHLVVKRZQEHORZ1RWLFHWKDWWKH,2LV PDSSHGWRDSDLURIREMHFWVEDVHGRQWKHW\SHRI,2VHOHFWHG7KHGHIDXOWLVWRXVHWKH FRQVROHEXWWKHFODVVLQLWLDOL]HUZLOODOVRDFFHSWDYDOLGVRFNHWRUVHULDO,2REMHFW #! /usr/bin/python # SimpleANSI.py # # A minimal set of functions for ANSI screen control. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

""" Simple VT100/Xterm ANSI terminal functions for Python.

This module is based on C code originally written for use with VxWorks running on embedded controllers. It is used to control the display of an ANSI-capable terminal or terminal emulator. It will work with Xterm on Unix and Linux systems, CygWin under Windows, and also with Tera Term under Windows.

The ANSITerm class supports I/O via an ANSI-capable console, a serial connection, or a network socket.

506 | Chapter 13:ಗUser Interfaces This is not a replacement for curses, and was never intended to be. It is a quick and simple way to put formatted data on a display; nothing more. It is useful for diagnostics, status displays, and simple command interfaces.

The pseudo-macro CSI is the ANSI "Command Sequence Introducer."

NOTE: This code has not been tested in all possible environments for all possible (or feasible) use cases. It may contain errors, omissions, or other unpleasant things. """ from sys import stdout import time

ESC = "\x1b" CSI = ESC+"["

CON = 0 SKT = 1 SIO = 2 class ANSITerm: """ Simple ANSI terminal control.

Supports I/O using the console, a network socket, or a serial port.

When communicating via a network socket, it is assumed that the physical port is a socket with send and receive methods, and that it has already been opened elsewhere.

When using a serial port, the port must already be open. In this case ioport must reference a valid pySerial object.

The default I/O mode is to use the console, which must support ANSI control sequences (otherwise the ANSI sequences will just be printed, not interpreted). Also note that all user input via the console requires that the Enter key be pressed when input is complete. This is an artifact of Python's raw_input() function, since it has no native getch()-type function.

Screen coordinates are specified as (row, col), or in other words, as (y, x). This is the same as how curses does it. """ def __init__(self, ioport=None, porttype=CON): """ Initialize the ANSITerm object.

If porttype is anything other than CON, ioport must reference a valid I/O port object.

If porttype is CON, self.port is assigned the value of None.

The defualt I/O method is the console.

Text-Based Interfaces | 507 """ self.pktsize = 1024 # just a default value for SKT mode self.port = ioport # SKT and SIO port object self.portOK = False # valid port indicator

# map to the appropriate I/O handlers if porttype == SKT: if self.port: self.portOK = True self.outfunc = self.__sktOutput self.inpfunc = self.__sktInput elif porttype == SIO: if self.port: self.portOK = True self.outfunc = self.__sioOutput self.inpfunc = self.__sioInput else: self.port = None self.portOK = True self.outfunc = self.__conOutput self.outflush = self.__conFlush self.inpfunc = self.__conInput

#------# I/O handlers #------# Although this could have been done without the use of a set of # one-line methods, this approach leaves the door open to easily # expand this scheme in the future: the inclusion of some type of # error handling, for example, or perhaps the ability to capture # and log data I/O. # # Note that the socket and serial I/O methods assume that a # standard Python socket object or a pySerial port object will # be used. Another type of I/O object may require different # methods for reading and writing. #------def __sktOutput(self, outstr): self.port.send(outstr)

def __sioOutput(self, outstr): self.port.write(outstr)

def __conOutput(self, outstr): stdout.write(outstr)

def __conFlush(self): stdout.flush()

def __sktInput(self): return self.port.recv(self.pktsize)

def __sioInput(self): return self.port.readline()

508 | Chapter 13:ಗUser Interfaces def __conInput(self): return raw_input() # no prompt is specified for raw_input

#------# Cursor positioning #------def moveHome(self): """ Move cursor to upper-left corner. """ if self.portOK: self.outfunc("%sH" % CSI) def moveNextline(self): """ Move to start of next line. """ if self.portOK: self.outfunc("%sE" % ESC) def movePos(self, row, col): """ Move cursor to screen location row, col. """ if self.portOK: self.outfunc("%s%d;%dH" % (CSI, row, col)) def moveUp(self, count): """ Move cursor up count rows. """ if self.portOK: self.outfunc("%s%dA" % (CSI, count)) def moveDown(self, count): """ Move cursor down count rows. """ if self.portOK: self.outfunc("%s%dB" % (CSI, count)) def moveFoward(self, count): """ Move cursor right count columns. """ if self.portOK: self.outfunc("%s%dC" % (CSI, count)) def moveBack(self, count): """ Move cursor left count columns. """ if self.portOK: self.outfunc("%s%dD" % (CSI, count)) def indexUp(self): """ Move/scroll up one line. """ if self.portOK: self.outfunc("%D" % ESC) def indexDown(self):

Text-Based Interfaces | 509 """Move/scroll down one line. """ if self.portOK: self.outfunc("%M" % ESC)

def savePos(self): """ Save current cursor position. """ if self.portOK: self.outfunc("%ss" % CSI);

def restorePos(self): """ Restore cursor to last saved position. """ if self.portOK: self.outfunc("%su" % CSI);

#------# Display control #------def clrScreen(self): if self.portOK: self.outfunc("%s2J" % CSI);

def clrEOL(self, row=None, col=None): """ Clear line from given or current position.

If row and col are None, the current position is used. """ if self.portOK: if row and col: self.movePos(row,col); self.outfunc("%s0K" % CSI)

def clrLine(self, row=None): """ Clear entire line (row) in display.

If row is None, the current position is used. """ if self.portOK: if row: self.movePos(row, 1) self.outfunc("%s2K" % CSI)

def resetDev(self): """ Reset terminal to initial state. """ if self.portOK: self.outfunc("%sc" % ESC)

#------# Input #------def readInput(self, reset=False): """ Get user input and echo it to the display.

510 | Chapter 13:ಗUser Interfaces If a prompt is required it must be generated at the appropriate location before this method is called.

If reset is True, when the input handler returns the cursor will be repositioned to the starting location prior to user input. This capability is provided mainly to compensate for the use of Python's raw_input() function when interacting with a console, as the user will need to press the Enter key to complete an input. """ instr = "" if self.portOK: if reset: self.savePos() instr = self.inpfunc() if reset: self.restorePos() return instr

#------# Output #------def writeOutput(self, outstr): """ Writes an arbitrary string at the current cursor position. """ if self.portOK: self.outfunc(outstr) self.outflush()

# self-test if __name__ == "__main__": term = ANSITerm(None, 0)

term.clrScreen()

# number the rows on the display from 1 to 24 for i in range(1,21): term.movePos(i,0) term.writeOutput("%0d" % i)

# write some text to the display term.movePos(14,4) term.writeOutput("* This is line 14, column 4") time.sleep(1) term.movePos(15,4) term.writeOutput("* This is line 15, column 4") time.sleep(1)

# create a diagonal series of characters for i in range(2,12): term.movePos(i,i+4) term.writeOutput("X") time.sleep(0.1)

for i in range(2,12):

Text-Based Interfaces | 511 term.movePos(i,i+4) term.writeOutput(" ") time.sleep(0.1)

for i in range(2,12): term.movePos(i,i+4) term.writeOutput("X") time.sleep(0.1)

# do some blinking the hard way for i in range(0,10): term.movePos(17,10) term.writeOutput("blick blink") time.sleep(0.5) term.clrEOL(17,10) time.sleep(0.5)

term.movePos(18,4) term.writeOutput("Did it blink? (y/n): ") instr = term.readInput() term.movePos(19,4) term.writeOutput("You answered %s, thank you for playing." % instr)

term.movePos(22,1) # and that's it

Using SimpleANSI 5HIHUULQJEDFNWR)LJXUHWKHILUVWWKLQJWRQRWLFHLVWKDWLWLVDIL[HGIRUPDWGLVSOD\ 2QHZD\WRFUHDWHWKLVW\SHRIGLVSOD\LVWRXVHDSUHGHILQHGWHPSODWHWRJHQHUDWHWKH EDVH VFUHHQ GLVSOD\ DQG FRQWLQXDOO\ UHGUDZ LW ZLWK QHZ GDWD 7KLV LV KRZ WKH console4.pyH[DPSOHGLGLW7KLVZRUNVEXWLW¦VPXFKPRUHHIILFLHQWLI\RXFDQMXVW GUDZWKHVWDWLFSRUWLRQVRIWKHGLVSOD\RQFHDQGRQO\FKDQJHWKHGDWDILHOGVDVQHFHVVDU\ console6.pyLQFRUSRUDWHVWKHANSITermFODVVWRKDQGOHYDULDEOHGDWDGLVSOD\DQGDFFHSW XVHULQSXW,WRQO\JHQHUDWHVWKHPDLQGLVSOD\VFUHHQRQFHDQGDOOVXEVHTXHQWFKDQJHV RFFXU DW VSHFLILF ORFDWLRQV LQ WKH PDLQ GLVSOD\ DUHD +HUH LV WKH VRXUFH OLVWLQJ IRU console6.py #!/usr/bin/python # console6.py # # Demonstrates the use of the SimpleANSI module. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

import random import time import datetime import threading import SimpleANSI

data_vals = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

512 | Chapter 13:ಗUser Interfaces currdate = "" currtime = "" updt_cnt1 = 0 updt_cnt2 = 0 ran = random.random def getDate(): global currdate

t = datetime.datetime.now() currdatetime = t.timetuple()

yr = str(currdatetime[0]) currdate = "%02d"%int(yr[2:]) + "%02d"%currdatetime[1] +\ "%02d"%currdatetime[2] def getTime(): global currtime

t = datetime.datetime.now() currdatetime = t.timetuple()

currtime = "%02d:"%currdatetime[3] + "%02d:"%currdatetime[4] +\ "%02d"%currdatetime[5]

# write data and time to display def writeDateTime(): getDate() getTime()

term.clrEOL(2,15) term.movePos(2,15) term.writeOutput("%s" % currdate) term.clrEOL(3,15) term.movePos(3,15) term.writeOutput("%s" % currtime)

# get simulated data input values def getDataVals(): global data_vals, updt_cnt1, updt_cnt2

data_vals[0] = ran() * 10.0 data_vals[1] = ran() * 10.0

if updt_cnt1 >= 4: for i in range(2,5): data_vals[i] = ran() * 10.0 updt_cnt1 = 0 else: updt_cnt1 += 1

if updt_cnt2 >= 10: for i in range(4,8): data_vals[i] = ran() * 10.0

Text-Based Interfaces | 513 updt_cnt2 = 0 else: updt_cnt2 += 1

# write channel data values def writeDataVals(): idx = 0 for i in range(6,14): term.movePos(i,10) term.writeOutput("%6.2f" % data_vals[idx]) idx += 1 # put cursor below display text when update is done term.movePos(16,1)

# generate the main display def mainScreen(): term.clrScreen()

term.movePos(1,1) term.writeOutput("Input Data Monitor")

term.movePos(2,1) term.writeOutput("Current Date:") term.movePos(3,1) term.writeOutput("Current Time:") writeDateTime()

term.movePos(5,1) term.writeOutput("Chan Last Value")

rownum = 1 for row in range(6,14): term.movePos(row,1) term.writeOutput(" %d" % rownum) rownum += 1

writeDataVals()

term.movePos(15,1) term.writeOutput("Enter X to exit.")

# raw_input() handler thread def getCommand(): global exit_loop

while True: instr = raw_input() if instr.upper() == 'X': exit_loop = True break time.sleep(0.1)

#------# Main loop #------

514 | Chapter 13:ಗUser Interfaces term = SimpleANSI.ANSITerm(None, 0)

exit_loop = False

# launch the raw_input() handler thread getinput = threading.Thread(target=getCommand) getinput.start()

mainScreen()

while exit_loop == False: writeDateTime() getDataVals() writeDataVals() time.sleep(0.2) console6.pyLVDFWXDOO\DVOLJKWO\PRUHUHDOLVWLFYHUVLRQRIWKHconsole4.pyH[DPSOH,¦YH UHXVHGWKHWKUHDGWULFNWRSUHYHQW3\WKRQ¦Vraw_input()PHWKRGIURPVXVSHQGLQJWKH PDLQORRSZKLOHZDLWLQJIRUXVHULQSXW3\WKRQ¦VGDWHDQGWLPHPHWKRGVDUHDOVRSXW WRXVH7KHSULPDU\REMHFWLYHKHUHLVWRVKRZKRZ$16,FRQWUROVHTXHQFHVYLDWKH SimpleANSIPRGXOHFDQEHXVHGWRJHQHUDWHDG\QDPLF$6&,,GLVSOD\EDVHGRQDVWDWLF WHPSODWH

Python and curses

7KLVGLVFXVVLRQRIcursesDQG3\WKRQ¦VcursesLPSOHPHQWDWLRQLVLQWHQ GHGPDLQO\IRU3\WKRQRQ8QL[/LQX[V\VWHPV3\WKRQZLOOQRWLPSRUW WKHcursesOLEUDU\PRGXOHRQD:LQGRZVV\VWHP WKH:LQGRZVYHUVLRQ RI3\WKRQGRHVQRWKDYHWKHcursesPRGXOH $OWKRXJKWKHUHDUHVRPH SRVVLEOHZRUNDURXQGV:LQGRZVDSSOLFDWLRQVVKRXOGSUREDEO\MXVWXVH VRPHWKLQJOLNHWKHSimpleANSIPRGXOHZLWKWKH$16,GOOGULYHU

7KLVVHFWLRQLVQ¦WDERXWSURJUDPPLQJIUXVWUDWLRQDOWKRXJK,KDYHQRGRXEWWKDWVRIW ZDUHGHYHORSPHQWKDVOHGWRVRPHYHU\FUHDWLYHFXUVLQJ1RWKLVVHFWLRQLVDERXWWKH cursesOLEUDU\IRUPDQLSXODWLQJFKDUDFWHUVRQDWHUPLQDOGLVSOD\XVLQJ$16,FRQWURO VHTXHQFHV :H¦YHMXVWVHHQVRPHRIZKDW\RXFDQGRXVLQJRQO\DKDQGIXORIVLPSOH$16,FRQWURO VHTXHQFHVDQGLI\RX¦YHHYHUXVHGWKHYL(PDFVRUHGWHGLWRUV\RX¦YHVHHQH[DPSOHV RIZKDWFDQEHGRQHZLWKWKHIXOOVHWRI$16,VHTXHQFHV$OWKRXJKWKHVXEVHWRIWKH $16,FRQWUROVHTXHQFHVZH¦YHXVHGVRIDUZLOOWDNHXVDORQJZD\UHDOL]LQJWKHIXOO FDSDELOLWLHVRIWKH$16,VHTXHQFHVZRXOGUHTXLUHVRPHVLJQLILFDQWSURJUDPPLQJ)RU WXQDWHO\LW¦VDOUHDG\EHHQGRQHIRUXVZLWKWKHcursesOLEUDU\ 7KHcursesOLEUDU\FDQEHXVHGWRGRWKLQJVOLNHPDQLSXODWHVSHFLILFDUHDVRIDGLVSOD\ ZKLOHOHDYLQJRWKHUDUHDVXQGLVWXUEHG LHcursesGRHVZLQGRZV FKDQJHWH[WVW\OHV DQGFRORUVVFUROOWH[WDQGRWKHUQHDWWULFNV

Text-Based Interfaces | 515 curses background cursesRULJLQDWHGRQ%6'8QL[V\VWHPVLQWKHHDUO\VDQGZDVODWHUDGRSWHGDQG H[WHQGHGIRU$77¦V6\VWHP95HOHDVH 69U 2Q/LQX[V\VWHPV$16,VFUHHQFRQ WUROLVSURYLGHGE\WKHOLEUDU\ ncursesVWDQGVIRU£QHZcurses¤DQRSHQVRXUFH UHSODFHPHQWIRUWKH69UcursesOLEUDU\ )URPQRZRQZKHQ,UHIHUWRcurses\RX VKRXOGWDNHLWDVPHDQLQJncurses ,WLVLPSRUWDQWWRQRWHWKDWcursesLVD8QL[/LQX[WKLQJDQGWKHUHLVQRQDWLYHSRUWRI WKHcursesOLEUDU\DYDLODEOHIRU:LQGRZV$WWHPSWLQJWRLPSRUWcursesLQWRD3\WKRQ SURJUDPUXQQLQJXQGHU:LQGRZVZLOOUHVXOWLQDQHUURUEHFDXVHWKHcursesOLEUDU\ PRGXOHLVQ¦WVXSSOLHGZLWKWKH:LQGRZVYHUVLRQRI3\WKRQ:KLOHWKHUHKDYHEHHQ VRPHDWWHPSWVDWFUHDWLQJcursesFRPSDWLEOHGLVSOD\VIRU:LQGRZV,¦YHQRWWULHGDQ\ RIWKHPDQG,FDQ¦WFRPPHQWRQWKHP

Python’s curses library module 3\WKRQ¦V curses OLEUDU\ PRGXOH LV VLPSO\ D ZUDSSHU DURXQG VRPH RI WKH VWDQGDUG ncursesOLEUDU\IXQFWLRQV,WLVQRWDFRPSOHWHZUDSSHUEXWLWGRHVLPSOHPHQWDIDLUO\ ODUJHQXPEHURIWKHPRVWFRPPRQO\XVHGIXQFWLRQV,ZRQ¦WWU\WRFRYHUDOORIWKH 3\WKRQcursesOLEUDU\PRGXOHKHUH,UHFRPPHQGWKDW\RXWDNHDORRNDWWKH3\WKRQ GRFXPHQWDWLRQWRJHWPRUHGHWDLOV:HZLOOORRNDWWKHEDVLFFRQFHSWVEHKLQGcurses DQGVHHKRZLWFDQEHXVHGWRPDQDJHDQ$6&,,VFUHHQGLVSOD\LQDQ;WHUPZLQGRZ $Q\3\WKRQSURJUDPWKDWXVHVcursesPXVWILUVWLQLWLDOL]HWKHcursesOLEUDU\DQGLQ VWDQWLDWHDcursesZLQGRZREMHFWWRPDQDJHWKHHQWLUHGLVSOD\DUHD7RHQGDcurses VHVVLRQDQGUHOHDVHFRQWURORIWKHGLVSOD\VFUHHQWKHendwin()IXQFWLRQLVFDOOHG7KLV LVVKRZQLQWKHIROORZLQJH[DPSOHZKLFKGRHVQRWKLQJEXWVWDUWXScursesDQGWKHQ VKXWLWGRZQDJDLQ import curses stdscr = curses.initscr()

# curses statements go here

curses.endwin() ,QcursesWKHSULPDU\ORJLFDOREMHFWLVWKHZLQGRZDQGWKHYDULRXVZLQGRZVLQDGLVSOD\ DUHDUUDQJHGDVDKLHUDUFK\ZLWKWKHILUVWZLQGRZ stdscrWKHHQWLUHGLVSOD\DUHD DV WKHSDUHQW)LJXUHLVDSVHXGR'LOOXVWUDWLRQRIWKLVDUUDQJHPHQW :ULWLQJDcursesEDVHGDSSOLFDWLRQLVEDVLFDOO\DPDWWHURIGHILQLQJWKHZLQGRZVWREH XVHGLPSOHPHQWLQJZKDWHYHUG\QDPLFFRQWUROWKH\PD\QHHG GURSGRZQPHQXVQHHG WRDSSHDUDWWKHDSSURSULDWHSODFHVWKHFXUVRUPD\QHHGWRUHVSRQGWRWKH7DENH\ DQGVRRQ DQGWKHQSRSXODWLQJWKHPZLWKWKHQHFHVVDU\WH[WDQGGDWDLQSXWILHOGV ,QWHUQDOO\cursesNHHSVWUDFNRIZKHUHWKLQJVDUHUHODWLYHWRHDFKZLQGRZVXFKDV FXUVRUSRVLWLRQ6RXQGVVLPSOHHQRXJKVROHW¦VWU\LWRXW

516 | Chapter 13:ಗUser Interfaces )LJXUHFXUVHVZLQGRZKLHUDUFK\ A simple data display using curses )LUVWRIIOHW¦VWUDQVODWHWKHconsole6.pyH[DPSOHLQWRcurses7KLVZLOODOORZXVWRVHH KRZWKHEDVLF$16,FRQWUROVHTXHQFHVZH¦YHDOUHDG\XVHGILWLQWRWKHcursesVFKHPH RIWKLQJV1RWHWKDWDOWKRXJKWKHRXWSXWZLOOORRNWKHVDPHLWZLOOQRORQJHUZRUNZLWK 06:LQGRZVIRUWKHUHDVRQVDOUHDG\VWDWHG cursesIXQFWLRQVDQGPHWKRGVFRPHLQWZRIRUPVPRGXOHOHYHOIXQFWLRQVWKDWSHUWDLQ WRWKHEHKDYLRURIWKHHQWLUHcursesPRGXOHDQGZLQGRZVSHFLILFPHWKRGVWKDWRSHUDWH RQDZLQGRZREMHFW:HGRQ¦WQHHGDOORIWKHIXQFWLRQDOLW\IURPcursesIRUWKLVSURMHFW RQO\DVXEVHW7DEOHOLVWVWKHGHILQLWLRQVIRUWKHcursesOLEUDU\IXQFWLRQVZH¦OOEH XVLQJLQLWLDOO\DQG7DEOHGHILQHVWKHZLQGRZREMHFWPHWKRGVZH¦OOQHHG6TXDUH EUDFNHWVLQGLFDWHRSWLRQDOSDUDPHWHUV

7DEOH6HOHFWHGFXUVHVOLEUDU\IXQFWLRQV Function Description cbreak() Enables cbreak mode. In this mode normal line buffering is disabled, and input characters may be read one by one from the input. Special key characters, such as Control-C, will still have the expected effect. nocbreak() Exits from cbreak mode and returns to the normal buffered input mode of operation. echo() Enables echo mode, wherein each character input is echoed to the display as the user enters it. noecho() Disables echo mode, so characters entered by the user are not echoed to the display. initscr() Initializes the curses module. The object returned (a WindowObject) represents the whole display screen. This is the top-level window. endwin() Shuts down curses and returns the terminal to its normal state. curs_set(visibility) Controls the visibility of the cursor. The visibility parameter can be one of 0, 1, or 2, with the effect being invisible, noticeable, or very visible, respectively. Exactly what “very visible” means depends on the terminal (or terminal emulator) in use. ungetch(ch) Pushes ch back into the input stream so that the next call to getch() will return it.

Text-Based Interfaces | 517 7DEOH6HOHFWHGFXUVHVZLQGRZPHWKRGV Method Description win.addstr([y, x], Writes str at [y, x], if specified, or at the current cursor location if not. Uses display str[, attr]) attributes attr, if given. Any existing characters on the screen are overwritten. win.clrtoeol() Erases all characters from the current cursor location to the end of the line. win.erase() Clears the entire window. win.getch([y, x]) Gets a character from the terminal with the cursor positioned at [y, x], if specified. The return value does not have to be a valid ASCII character, but could be some value > 255 for function keys and such. In no-delay mode (nonblocking, set by the nodelay() method), a value of −1 is returned if there is no input available; otherwise getch() will block and wait until a key is pressed. Suspect the return of being an integer until proven otherwise. win.getstr([y, x]) Reads and returns a string from the user with the cursor initially positioned at [y, x], if specified. Provides limited line-editing capability. win.move(new_y, new_x) Moves the cursor to position (new_y, new_x) in the window. win.nodelay(yes) Controls the blocking behavior of getch(). If yes is 1,getch() will be a nonblocking call. win.subwin([nlines, Returns a subwindow object. The upper-left corner is defined by (begin_y, begin_x), ncols], begin_y, and the width and height are defined by ncols and nlines, respectively. If ncols and begin_x) nlines are omitted, the new window will extend to the lower-right corner of the display area.

,W¦VLQWHUHVWLQJWRREVHUYHWKDWANSITermDFWXDOO\SURYLGHVPRUHORZOHYHOIXQFWLRQDOLW\ WKDQcurses,QRWKHUZRUGVWKHcursesOLEUDU\HQFDSVXODWHVPDQ\RIWKHORZOHYHO$16, FRQWUROVHTXHQFHVLQLWVPHWKRGVZKHUHDVANSITermH[SRVHVWKHP7KLVVKRXOGQ¦WEH WRRVXUSULVLQJVLQFHANSITermLVUHDOO\QRWKLQJPRUHWKDQDIDQF\ZUDSSHUDURXQGWKH $16,VHTXHQFHVDQGGRHVQRWSURYLGHDQ\VXSSRUWIRUZLQGRZPDQDJHPHQWRURWKHU KLJKOHYHOIXQFWLRQV 7KHILUVWH[DPSOHcurses1.pyGRHVQ¦WGRDQ\WKLQJIDQF\,WMXVWFUHDWHVDFRQWLQXRXVO\ XSGDWHGGLVSOD\OLNHWKHRQHWKDWconsole6.pyJHQHUDWHV #!/usr/bin/python # curses1.py # # Demonstrates the use of Python's curses module. # # Translated from the console6.py example. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

import random import time import datetime import curses import traceback

data_vals = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

518 | Chapter 13:ಗUser Interfaces currdate = "" currtime = "" updt_cnt1 = 0 updt_cnt2 = 0 ran = random.random def getDate(): global currdate

t = datetime.datetime.now() currdatetime = t.timetuple() yr = str(currdatetime[0]) currdate = "%02d"%int(yr[2:]) + "%02d"%currdatetime[1] +\ "%02d"%currdatetime[2] def getTime(): global currtime

t = datetime.datetime.now() currdatetime = t.timetuple() currtime = "%02d:"%currdatetime[3] + "%02d:"%currdatetime[4] +\ "%02d"%currdatetime[5]

# write data and time to display def writeDateTime(win): getDate() getTime()

win.move(2,15) win.clrtoeol() win.addstr("%s" % currdate) win.move(3,15) win.clrtoeol() win.addstr("%s" % currtime) win.refresh()

# get simulated data input values def getDataVals(): global data_vals, updt_cnt1, updt_cnt2

data_vals[0] = ran() * 10.0 data_vals[1] = ran() * 10.0

if updt_cnt1 >= 4: for i in range(2,5): data_vals[i] = ran() * 10.0 updt_cnt1 = 0 else: updt_cnt1 += 1

if updt_cnt2 >= 10: for i in range(4,8): data_vals[i] = ran() * 10.0 updt_cnt2 = 0

Text-Based Interfaces | 519 else: updt_cnt2 += 1

# write channel data values def writeDataVals(win): idx = 0 for i in range(6,14): win.move(i,10) win.clrtoeol() win.addstr("%6.2f" % data_vals[idx]) idx += 1

win.refresh() # put cursor below display text when update is done win.move(16,1)

# generate the main display def mainScreen(win): win.erase()

win.move(1,1) win.addstr("Input Data Monitor") win.refresh()

win.move(2,1) win.addstr("Current Date:") win.move(3,1) win.addstr("Current Time:") win.refresh()

writeDateTime(win)

win.move(5,1) win.addstr("Chan Last Value") win.refresh()

rownum = 1 for row in range(6,14): win.move(row, 1) win.addstr(" %d" % rownum) rownum += 1 win.refresh()

writeDataVals(win)

win.move(15,1) win.addstr("Enter X to exit.") win.refresh()

def mainloop(win): win.nodelay(1) # disable getch() blocking # draw the main display template mainScreen(win)

# run until the user wants to quit

520 | Chapter 13:ಗUser Interfaces while 1: # check for keyboard input inch = win.getch() # getch() will return −1 if no character is available if inch != −1: # see if inch is really the exit character instr = hr(inch) if instr.upper() == 'X': break writeDateTime(win) getDataVals() writeDataVals(win) time.sleep(0.2)

def startup(): # Borrowed the idea of using a try-except wrapper around the # initialization from David Mertz try: # Initialize curses stdscr = curses.initscr()

# Turn off echoing of keys and enter cbreak mode, # where no buffering is performed on keyboard input curses.noecho() curses.cbreak()

mainloop(stdscr) # Enter the main loop

# Set everything back to normal curses.echo() curses.nocbreak()

curses.endwin() # Terminate curses except: # In event of error, restore terminal to sane state curses.echo() curses.nocbreak() curses.endwin() traceback.print_exc() # Print the exception

if __name__=='__main__': startup() ,QcursesWKHVFUHHQFRRUGLQDWHVDUHVSHFLILHGLQ \[ RUGHU¢LH URZFROXPQ ¢ ZKLFKLVDOVRKRZWKHANSITermFODVVGRHVLW$IWHUWKHPDLQZLQGRZREMHFW stdscr LV FUHDWHGWKHFDOOVWRWKHcursesIXQFWLRQVnoecho()DQGcbreak()GLVDEOHORFDOHFKRDQG LQSXWEXIIHULQJ)LQDOO\QRWLFHWKDWmainloop()LVFDOOHGZLWKstdscrDVLWVVROHSDUDP HWHU$OOVXEVHTXHQWZLQGRZPHWKRGVXVHGDUHPHWKRGVRIWKLVREMHFW %HFDXVHRIWKHZD\WKDWcursesPDQDJHVZLQGRZREMHFWVLQWHUQDOO\LWZRQ¦WDXWRPDW LFDOO\VHQGRXWSXWWRWKHVFUHHQ\RXKDYHWRWHOOLWZKHQWRXSGDWHWKHGLVSOD\3DUWRI WKHUHDVRQIRUWKLVOLHVLQKRZDFRPSOH[cursesGULYHQGLVSOD\PLJKWEHXVHG5DWKHU WKDQKDYHLQGLYLGXDOOLQHVIOLFNHULQJRQWKHVFUHHQDVGDWDLVXSGDWHGWKHrefresh()

Text-Based Interfaces | 521 PHWKRGFDQEHXVHGWRXSGDWHWKHGLVSOD\VRWKDWFKDQJHVRFFXURYHUDVHWRIGLVSOD\ LWHPVRULQGLYLGXDOZLQGRZVDQGDOODWRQFH7KHRWKHUSDUWRIWKHUHDVRQIRUXVLQJ refresh() DQGWKHRWKHUUHODWHGPHWKRGV LVWKDWZKHQDPHWKRGOLNHaddstr()LVFDOOHG LWFKDQJHVWKHGDWDLQWKHLQWHUQDOUHSUHVHQWDWLRQRIWKHGLVSOD\IRUDSDUWLFXODUZLQGRZ EXWLWGRHVQ¦WDXWRPDWLFDOO\SDVVWKDWFKDQJHWRWKHGLVSOD\

Adding a subwindow /HW¦VDVVXPHWKDW\RXZDQWWRKDYHDVXEZLQGRZWKDWSRSVXSLQUHVSRQVHWRVRPHXVHU LQSXW7KHILUVWVWHSLVWRGHILQHWKHZLQGRZ,Qcurses2.py,¦YHHQFDSVXODWHGWKHVXE ZLQGRZREMHFWLQLWVRZQIXQFWLRQopenSubWindow()ZKLFKUHVLGHVLQWKHFRGHMXVW SULRUWRmainloop() def openSubWindow(win): # create a subwindow and keep it open until user presses the X # key subwin = win.subwin(10, 30, 10, 10) subwin.nodelay(1) # disable getch() blocking subwin.erase() subwin.bkgdset(' ') subwin.refresh() subwin.addstr(3, 0, "Enter X to exit subwindow") subwin.refresh() while 1: inch = subwin.getch() if inch != −1: instr = chr(inch) if instr.upper() == 'X': break time.sleep(0.2) 7KLVQHZIXQFWLRQLVFDOOHGIURPZLWKLQWKHmainloop()IXQFWLRQE\DGGLQJDWFRPPDQG WRWKHLQSXWFRPPDQGVHWOLNHVR def mainloop(win): win.nodelay(1) # disable getch() blocking # draw the main display template mainScreen(win)

# run until the user wants to quit while 1: # check for keyboard input inch = win.getch() # getch() will return −1 if no character is available if inch != −1: # see if inch is really the exit character instr = chr(inch) if instr.upper() == 'X': break if instr.upper() == 'W': openSubWindow(win) # simple way to restore underlying main screen

522 | Chapter 13:ಗUser Interfaces mainScreen(win) writeDateTime(win) getDataVals() writeDataVals(win) time.sleep(0.2) :KHQWKHXVHUSUHVVHVWKHw RUW NH\KHZLOOVHHDFOHDUÜFKDUDFWHUUHJLRQRIWKH GLVSOD\DSSHDUZLWKWKHVLQJOHOLQH£(QWHU;WRH[LWVXEZLQGRZ¤:KHQWKHVXEZLQGRZ IXQFWLRQH[LWVWKHmainloop()IXQFWLRQUHGUDZVWKHGLVSOD\WRFRYHUXSWKHKROHOHIWE\ WKHVXEZLQGRZ 7KHUHDUHRIFRXUVHPRUHHOHJDQWZD\VWRKDQGOHFUHDWLQJDQGGHOHWLQJVXEZLQGRZV EXW,FKRVHWKLVDSSURDFKWRNHHSWKLQJVVLPSOH7KHcurses2.pyH[DPSOHDOVRGRHVQRW GRWKLQJVOLNHGUDZDERUGHUDURXQGWKHVXEZLQGRZRUILOOLQWKHEDFNJURXQGZLWKD VROLGVKDGHRUFRORU+RZWKHVHIHDWXUHVDQGRWKHUVLQWKHcursesOLEUDU\ZLOOEHKDYHLV ODUJHO\GHSHQGHQWRQKRZWKHGLVSOD\KDQGOHV$16,FRPPDQGVHTXHQFHVZKLFKLQ WXUQGHSHQGVRQWKHV\VWHP¦VGHILQLWLRQV,I\RXSODQWRXVHcursesIRU\RXU DSSOLFDWLRQV,ZRXOGHQFRXUDJH\RXWRVSHQGVRPHWLPHZLWK3\WKRQ¦VcursesPRGXOH DQGWKHterminfoPDQSDJH$OVREHVXUHWRFKHFNRXWWKHVHFWLRQ£6XJJHVWHG5HDG LQJ¤RQSDJH

To Curse or Not to Curse, Is That the Question? ,I\RXGRQ¦WQHHGWKHDGYDQFHGIXQFWLRQDOLW\IRXQGLQcursesDQG\RXZDQW\RXUDS SOLFDWLRQWREHSRUWDEOHZLWKRXWUHVRUWLQJWRDIXOORQ*8,\RXPD\ZDQWWRFRQVLGHU MXVWXVLQJVRPHWKLQJOLNHSimpleANSIDQGEHGRQHZLWKLW0RVWLQVWUXPHQWDWLRQDSSOL FDWLRQVGRQ¦WUHDOO\QHHGDORWRIIDQF\VFUHHQFRQWUROVDQ\ZD\DQGZH¦YHDOUHDG\VHHQ KRZWRFUHDWHGDWDGLVSOD\VFUHHQVZLWKUHDOWLPHXSGDWHFDSDELOLWLHV +RZHYHU WKDW EHLQJ VDLG curses RIIHUV VRPH FDSDELOLWLHV WKDW RQH ZRXOG EH KDUG SUHVVHGWRGXSOLFDWHZLWKDVLPSOH$16,FRQWUROOLEUDU\DWOHDVWQRWZLWKRXWVRPH VHULRXVFRGLQJ6LQFHcursesLVDOUHDG\ZULWWHQDQGYHU\PDWXUHLWPDNHVPRUHVHQVH WRXVHWKDWLI\RXQHHGVXEZLQGRZVPHQXVPRXVHFRQWURODQGGLDORJVLQ\RXUDSSOL FDWLRQDQG\RXGRQ¦WQHHGLWWRUXQRQ:LQGRZV :KLOHcursesLVQRWDFURVVSODWIRUPVROXWLRQLWLVDFURVVGLVSOD\LQWHUIDFH$Q\V\VWHP ZLWKWKHDELOLW\WRGLVSOD\$6&,,FKDUDFWHUVDQGLQWHUSUHW$16,FRQWUROVHTXHQFHVFDQ SUREDEO\KDQGOHDcursesGULYHQGLVSOD\7KLVLQFOXGHVWHUPLQDOHPXODWRUVUXQQLQJ XQGHU:LQGRZVZLWK97HPXODWLRQFDSDELOLW\;WHUPZLQGRZVRQ/LQX[V\VWHPV DQG\HVHYHQDGXPEWHUPLQDOOLNHD97 LI\RXKDSSHQWRKDYHRQHKDQG\WKDWLV  ,QDQLQVWUXPHQWDWLRQV\VWHPXVLQJDQ$16,$6&,,WHUPLQDORUWHUPLQDOHPXODWRU FRQQHFWHGWRDUHPRWHKRVWV\VWHPYLDDVHULDOSRUWRUQHWZRUNFRQQHFWLRQLVQRWDVGDIW DVLWPLJKWVRXQGDWILUVW6HUYHUVLQODUJHLQVWDOODWLRQVRIWHQXVHcursesIRUV\VWHPVWDWXV GLVSOD\DQGFRQWUROLQWHUIDFHVDOORZLQJWKHRSHUDWRUDFFHVVWRWKH26HYHQZKHQQR PDLQFRQVROH DQGKHQFHQR*8, LVDYDLODEOH

Text-Based Interfaces | 523 ,QDQLQVWUXPHQWDWLRQV\VWHP\RXPD\ZDQWWREHDEOHWRPRQLWRURUFRQWURODUHPRWH V\VWHPXVLQJDVLPSOHFRPPXQLFDWLRQVSURWRFRORYHUDWKLQFDEOHUDWKHUWKDQWU\LQJ WRVQDNHDKHIW\FDEOHEXQGOHEHWZHHQWKHFRQWUROOHU3&DQGWKHYDULRXVVHQVRUVDQG DFWXDWRUV2ULWMXVWPLJKWQRWEHIHDVLEOHWREHFRORFDWHGZLWKWKHKRVWPDFKLQHIRU ZKDWHYHUUHDVRQV H[SORVLRQKD]DUGQRLVHUDGLDWLRQKHDWDQGFU\RJHQLFFRQGLWLRQV DUHDIHZWKDWVSULQJWRPLQG 7KH$16,FRQWUROVHTXHQFHVRIIHUDTXLFNDQGUHODWLYHO\ HDV\ZD\WRJHWDUHDGDEOHDQGSHUKDSVHYHQHOHJDQWFRQWURODQGGDWDGLVSOD\XSDQG UXQQLQJTXLFNO\ 0\DSRORJLHVWR:6KDNHVSHDUH Graphical User Interfaces $OOPRGHUQJHQHUDOSXUSRVHRSHUDWLQJV\VWHPVLQFRUSRUDWHVRPHW\SHRIJUDSKLFDOXVHU LQWHUIDFH *8, LQWRWKHLUGHVLJQ2QO\WKRVHRSHUDWLQJV\VWHPVLQWHQGHGIRUGHHSO\ HPEHGGHGDSSOLFDWLRQVGRQ¦WFRPHZLWKVRPHNLQGRI*8,VLQFHWKH\GRQ¦WQHHGRQH DQ\ZD\ WKH\XVXDOO\GRQ¦WKDYHDQ\NLQGRIXVHULQWHUIDFHGLVSOD\DFWXDOO\  7KH*8,LVDOD\HURIIXQFWLRQDOLW\RQWRSRIWKHFRUHRSHUDWLQJV\VWHPDQGLQVRPH FDVHVLWPD\HYHQEHRSWLRQDO,QD8QL[RU/LQX[V\VWHPWKH*8,LVVWDUWHGDIWHUWKH RSHUDWLQJV\VWHPORDGVDVDGLVFUHWHVWHSLQWKHERRWVHTXHQFHDQGD/LQX[V\VWHPZLOO UXQMXVWILQHZLWKRXWWKH*8,,QRWKHUFDVHVVXFKDVZLWK:LQGRZVWKH*8,LVWLJKWO\ LQWHJUDWHGLQWRWKH26DQGZKLOHVWLOOWHFKQLFDOO\DOD\HURIIXQFWLRQDOLW\LWLVQRWGH VLJQHGWREHHDVLO\GLVDEOHG ,QWKLVVHFWLRQZH¦OOVHHKRZWRXVHD*8,ZLWK3\WKRQ,ZRQ¦WJRLQWRGHWDLOHGFRGH H[DPSOHVPDLQO\EHFDXVHDQ\WKLQJEH\RQGDVLPSOH£+HOOR:RUOG¤*8,FDQJHWUDWKHU LQYROYHG:KDWZHZLOOGRLVORRNDWZKHUHWKHZKROH*8,FRQFHSWRULJLQDWHGDQGZK\ \RXPLJKWZDQWWRXVHRQHUDWKHUWKDQVLPSOHFRPPDQGOLQHRU$16,VROXWLRQVOLNH WKHRQHVZH¦UHDOUHDG\VHHQLQWKLVFKDSWHU:HZLOOZUDSXSZLWKDORRNDWVRPHVLPSOH GDWDGLVSOD\*8,VWRJLYH\RXDVHQVHRIZKDWLVLQYROYHGLQFUHDWLQJD*8,

Some GUI Background and Concepts %HIRUHXQGHUWDNLQJDTXLFNVXUYH\RIDFRXSOHRI*8,WRRONLWVIRU3\WKRQ,¦GOLNHWR WDNHDORRNDWWKHKLVWRU\RIWKH*8,DVZHNQRZLWWRGD\0DQ\SHRSOHVWLOOEHOLHYHWKDW $SSOHLQYHQWHGWKH*8,EXWWKLVLVQRWWUXH7KHILUVWUHDOO\XVDEOH*8,ZDVLQYHQWHG DW;HUR[¦V3DOR$OWR5HVHDUFK&HQWHU 3$5& LQWKHHDUO\VDQGUDQRQ;HUR[¦V $OWRFRPSXWHUV\VWHP6WHYH-REVVDZLWGXULQJDWRXURIWKH3$5&IDFLOLW\LQDQG WKH$SSOH/LVDDQGODWHUWKH0DFFDPHLQWRH[LVWHQFHVKRUWO\WKHUHDIWHU:KHQ0L FURVRIWJRWZLQGRIZKDW$SSOHZDVXSWRWKHGHFLVLRQZDVPDGHWRHPEDUNRQWKH GHYHORSPHQWRI:LQGRZVZKLFKZRXOGHYHQWXDOO\UHQGHUWKH'26FRPPDQGOLQH REVROHWH7KH;:LQGRZ6\VWHPIRXQGLQLWLDOO\RQ8QL[V\VWHPVDQGODWHURQ/LQX[ ZDVDQRWKHUUHVSRQVHWRWKHSXVKWRFUHDWHDJUDSKLFDOLQWHUIDFHH[SHULHQFHIRUWKHXVHU

524 | Chapter 13:ಗUser Interfaces ;HUR[LWVHOIZDVXQDEOHWRJHWDQDIIRUGDEOHV\VWHPWRPDUNHWLQWLPHWRFDSLWDOL]HRQ LWVRZQLQYHQWLRQ 7KHUHDUHPXOWLSOHOHYHOVRIDEVWUDFWLRQLQDPRGHUQFRPSXWHUV\VWHP¦VJUDSKLFDOLQ WHUIDFH7KH*8,LVUHVSRQVLEOHIRUPDQDJLQJWKHSODFHPHQWRIDSSOLFDWLRQGULYHQZLQ GRZVRQWKHSULPDU\GLVSOD\GHYLFHNHHSLQJWUDFNRIWKHVRFDOOHG]RUGHU KRZWKH ZLQGRZVRYHUODSDQGVWDFNRQWRSRIRQHDQRWKHU DQGPDNLQJVXUHWKDWZLQGRZVJHW XSGDWHQRWLFHVZKHQWKH\QHHGWRUHGUDZSRUWLRQVRIWKHLUGLVSOD\DUHD(DFKDSSOLFD WLRQZLQGRZKDVLWVRZQPHFKDQLVPVIRUKDQGOLQJXVHULQSXWGDWDRXWSXWDQGUHGUDZ RUUHIUHVKDFWLYLWLHV,QVRPHW\SHVRIZLQGRZGLVSOD\VVXFKDVWKHPXOWLSOHGRFXPHQW LQWHUIDFH 0', WKHDSSOLFDWLRQ¦VZLQGRZLVD£SDUHQW¤ZLQGRZWKDWPD\FRQWDLQRQH RUPRUH£FKLOG¤ZLQGRZVHDFKRIZKLFKPLJKWKDYHDPHQXEDUEXWWRQVGLDORJV LPDJHGLVSOD\VDQGRWKHUFRPSRQHQWV NQRZQDVZLGJHWV (DFKOHYHOKDVLWVRZQVHW RIGLVSOD\PDQDJHPHQWIXQFWLRQVDQGFDQFRPPXQLFDWHZLWKRWKHUZLQGRZVZLWKLQ WKHFRQWH[WRIWKHSDUHQWZLQGRZRUWKHRYHUDOOWRSOHYHOV\VWHP*8, $JUDSKLFDOXVHULQWHUIDFHLVQRWRQO\SUHWWLHUWKDQDFRPPDQGOLQHRUWH[WEDVHGLQ WHUIDFHLWFDQDOVREHPRUHLQWXLWLYHDQGHDVLHUWRFRPSUHKHQG+RZHYHUDV\RXPD\ KDYHJDWKHUHGIURPWKHSUHYLRXVSDUDJUDSKWKLVHDVHRIXVHGRHVQRWFRPHZLWKRXWD SULFH $*8,LVPXFKPRUHFRPSOLFDWHGWKDQDIXQFWLRQDOO\HTXLYDOHQWWH[WEDVHGLQWHUIDFH $*8,LVDOVRPRUHWKDQMXVWDQDGGHGELWRIIXQFWLRQDOLW\IRUDQDSSOLFDWLRQ¢LQPRVW FDVHVLWLVWKHDSSOLFDWLRQ,QRWKHUZRUGVLI\RXZDQWWRDGGDQ$6&,,WH[W¡W\SHLQ WHUIDFHWRDSURJUDPLW¦VEDVLFDOO\MXVWDPDWWHURIXVLQJ$16,WHUPLQDOFRQWUROVHTXHQ FHVWRPDQLSXODWHDGLVSOD\XVLQJFRQYHQWLRQDO,2FDOOV7KHUH¦VQRWKLQJUHDOO\VSHFLDO DERXWLW,QD*8,WKHZLQGRZPDQDJHUQRWRQO\KDQGOHVWKHPDQDJHPHQWRIWKH JUDSKLFDOFRPSRQHQWVLQWKHGLVSOD\EXWDOVRHQFDSVXODWHVWKHIXQFWLRQDOLW\RIWKH DSSOLFDWLRQZLWKLQLWVIUDPHZRUN,I\RXZDQWWRKDYH£RXWERDUG¤IXQFWLRQDOLW\ZLWKD *8,\RXZLOOQHHGWRVWDUWWKLQNLQJDERXWH[WHUQDOSURFHVVHVQDPHGSLSHVVRFNHWV DQGRWKHUIRUPVRILQWHUSURFHVVFRPPXQLFDWLRQ ,3& WRLPSOHPHQW\RXUVFKHPH/LIH ZLWKD*8,FDQJHWYHU\FRPSOLFDWHGYHU\IDVW 6RZK\ZRXOG\RXZDQWWRXVHD*8,"7KHWZRPDLQUHDVRQVDUHILGHOLW\DQGIXQF WLRQDOLW\:LWKD*8,\RXFDQGLVSOD\GDWDPXFKPRUHDFFXUDWHO\WKDQ\RXFDQZLWK DQ$6&,,GLVSOD\*UDSKVDUHDJRRGH[DPSOHRIWKLV$*8,LVDOVRFDSDEOHRIGLVSOD\LQJ LPDJHVDQGDOORZLQJWKHXVHUWRLQWHUDFWZLWKWKHLPDJHVDVFRQWUROLQSXWV0DQ\KLJK HQGLQGXVWULDOFRQWUROV\VWHPVXVHLQWHUIDFHVOLNHWKLVZKHUHDGLDJUDPRIDSODQW¦V SURFHVVFRPSRQHQWVDUHGLVSOD\HGZLWKUHDOWLPHGDWDDQGWKHXVHUFDQVHOHFWDFRP SRQHQWDQGFKDQJHLWVRSHUDWLQJSDUDPHWHUVE\FOLFNLQJRQLWVLPDJHLQWKHGLDJUDP :LWKD*8,\RXFDQDOVRSUHVHQWD'JUDSKRUHYHQDVROLGPRGHOWKDWWKHXVHUFDQ URWDWHWRYLHZLWIURPGLIIHUHQWDQJOHVSDQDURXQGDQG]RRPLQRURXWZLWKWKHGLVSOD\ FRQWLQXRXVO\XSGDWHG6RPHODVHULQWHUIHURPHWHUV\VWHPVXVHGLVSOD\VOLNHWKLVWRVKRZ WKH PLQXWH IHDWXUHV RQ DQ RSWLFDO VXUIDFH ZKLOH FRQWLQXRXVO\ DFTXLULQJ QHZ PHDVXUHPHQWV

Graphical User Interfaces | 525 :KLOHWKHVHDUHDOOYHU\LPSUHVVLYHIHDWXUHV,¦PJRLQJWRVWLFNWR*8,GLVSOD\VWKDWDUH HDV\WRLPSOHPHQW,I\RXKDYHWKHGHVLUHWROHDUQPRUHVHHWKHVHFWLRQ£6XJJHVWHG 5HDGLQJ¤RQSDJH

Using a GUI with Python ,Q WKLV VHFWLRQ , ZLOO SUHVHQW WZR FRPPRQ *8, WRRONLWV IRU 3\WKRQ 7N,QWHU DQG Z[3\WKRQ$OWKRXJKWKHHQGUHVXOWRIERWKLVHVVHQWLDOO\WKHVDPHWKHEDVLFGLIIHUHQFHV EHWZHHQKRZWKHWRRONLWVDUHXVHGDQGZKDWFDQEHGRQHZLWKWKHPDUHWKLQJV\RX VKRXOGEHDZDUHRIZKHQGHFLGLQJRQZKLFKRQHWRXVH:H¦OODOVRORRNDWVRPHRIWKH WRROVDYDLODEOHWRKHOSZLWKGLVSOD\GHVLJQDQGOD\RXW %HIRUHPRYLQJRQZHQHHGVRPHWHUPLQRORJ\%XWILUVW,¦GOLNHWRSRLQWRXWWKDWPDQ\ RIWKHVHWHUPVDUHXVHGLQWHUFKDQJHDEO\VRPHWLPHVLQFRUUHFWO\VR,ZLOOWU\WREHFRQ VLVWHQWLQWKLVFKDSWHUDQGKRSHIXOO\UHGXFHWKHSRWHQWLDOIRUFRQIXVLRQ 7KHUHDUHEDVLFDOO\WZRW\SHVRIREMHFWVLQD*8,FRQWDLQHUVDQGFRQWUROV$FRQWDLQHU LVXVHGWRJURXSRWKHUFRQWDLQHUVDQGFRQWUROVDQGWRPDLQWDLQWKHIXQFWLRQDOUHODWLRQ VKLSVEHWZHHQWKH*8,REMHFWVZLWKLQLW&RQWDLQHUVLQFOXGHZLQGRZVIUDPHVGLDORJV DQGPRGDOZLQGRZV$GLVWLQJXLVKLQJFKDUDFWHULVWLFRIDZLQGRZOLNHREMHFWLVWKDWLW FDQH[LVWRQLWVRZQDVWKHWRSOHYHOREMHFWLQDKLHUDUFK\RIGLVSOD\REMHFWV &RQWUROVDUHWKRVH*8,REMHFWVWKDWSURYLGHDQLQWHUDFWLRQSRLQWIRUDXVHU7KH\DUH DOVRNQRZQDV£ZLGJHWV¤ZKLFKLVKRZZH¦OOUHIHUWRWKHP$ZLGJHWPLJKWEHDEXWWRQ DVFUROOEDURUDFDQYDVIRUGUDZLQJ7KHWHUPFDQDOVRUHIHUWRDWH[WHQWU\ILHOGRUD FRPSOH[FRPSRVLWHREMHFWFRPSRVHGRIRWKHUVLPSOHUZLGJHWV,QVRPHWRRONLWVWKHUH DUHDOVRVSHFLDOFRQWDLQHUZLGJHWVWKDWDUHQ¦WUHDOO\ZLQGRZVEXWFDQEHXVHGWRORJLFDOO\ JURXSDVHWRIIXQFWLRQDOO\UHODWHGZLGJHWV7KHSDQHOZLGJHWLVRQHVXFKREMHFW0HP EHUVRIWKHVHWRIREMHFWVFDOOHGFRQWUROVH[LVWLQWKHFRQWH[WRIDZLQGRZREMHFW 1DWXUDOO\WKHUHDUHH[FHSWLRQVDQGVSHFLDOFDVHVDQGWKLQJVDUHQ¦WDOZD\VVRQHDWO\ DQGFOHDUO\GHOLQHDWHG)RUH[DPSOHVRPHZLQGRZREMHFWVVXFKDVIUDPHVFDQEHERWK DWRSOHYHOREMHFWDQGDVXERUGLQDWHWRDQRWKHUZLQGRZ,QRWKHUFDVHVDZLGJHWPLJKW EHDEOHWRDFWDVDFRQWDLQHUHYHQWKRXJKLWFDQQRWVWDQGDORQHZLWKRXWDSDUHQWZLQ GRZ,WPLJKWKHOSWRNHHSLQPLQGWKDW*8,FRPSRQHQWVLQ3\WKRQDUHREMHFWVLQWKH WUXHVHQVHRIWKHZRUGDQGLWLVSRVVLEOHDQGTXLWHFRPPRQWRFUHDWHQHZREMHFWVE\ LQKHULWLQJIURPH[LVWLQJFODVVGHILQLWLRQV2QFH\RXJHWDVHQVHRIKRZD*8,ZRUNV DQGZKDWJRHVLQWRPDNLQJRQHWKHJUD\DUHDVZLOOWHQGWREHFRPHOHVVERWKHUVRPH ,QWKHPHDQWLPH\RXFDQJHQHUDOO\DVVXPHWKDWDZLQGRZREMHFWLVZKHUHZLGJHWVDQG RWKHUZLQGRZVDUHORFDWHGDQGWKDWZLQGRZVGRQ¦WKDYHFRQWUROLQSXWVXQOHVVWKH\ DOVRKDYHZLGJHWV 7KHUHDUHEDVLFDOO\WKUHHZD\VWRORRNDWD*8,DV22EDVHGVRIWZDUHZLWKFKLOGFODVVHV GHULYHGIURPSDUHQWFODVVGHILQLWLRQVDVDVWUXFWXUHRIKLHUDUFKLFDOO\RUJDQL]HGREMHFWV VKDULQJDGLVSOD\VSDFHEXWQRWQHFHVVDULO\UHODWHGWRRQHDQRWKHULQDQ22LQKHULWDQFH

526 | Chapter 13:ಗUser Interfaces VHQVHDQGIXQFWLRQDOO\DVDQHYHQWGULYHQVRIWZDUHDSSOLFDWLRQ:H¦OOVWDUWZLWKWKH 22YLHZ

GUI objects ,QWRRONLWVWKDWHPSOR\DQREMHFWRULHQWHGDSSURDFKHDFKZLQGRZRUZLGJHWLVGHILQHG DVDFODVVDQGHDFKFODVVSURYLGHVDVHWRIPHWKRGVDQGDWWULEXWHVZ[3\WKRQLVRQHRI WKHVHDQGWKHXQGHUO\LQJZ[:LGJHWVLVYHU\PXFKDQ22GHVLJQ,QLWLDOO\WKH7NWRRONLW ZDVQ¦WDWUXH22LPSOHPHQWDWLRQ WFOLVQ¦WDQ22ODQJXDJH EXWZLWK7N,QWHULWWRR KDVDFTXLUHGWKHWUDSSLQJVRIFODVVHVREMHFWVDQGPHWKRGV7DNLQJDQ22DSSURDFK DOVRPHDQVWKDW\RXFDQFUHDWHDQHZZLGJHWFODVVE\LQKHULWLQJIURPDQH[LVWLQJRQH DQGSHUKDSVRYHUULGHH[LVWLQJPHWKRGVRUDGGVRPHQHZRQHV ,QPRVWFDVHVDZLGJHWZLOOEHVXEFODVVHGIURPDZLQGRZRUZLGJHWWKDWLWVHOILVGHULYHG IURPDSDUHQWFODVV7KXVDIUDPHREMHFWFRXOGEHVXEFODVVHGIURPDZLQGRZREMHFW DQGDEXWWRQZLGJHWREMHFWPLJKWEHVXEFODVVHGIURPDFRQWUROREMHFW VHH)LJXUH 

)LJXUH*8,REMHFWLQKHULWDQFHV

1RWDOO*8,LPSOHPHQWDWLRQVIROORZWKLVSDUWLFXODUVFKHPHZKLFKLQWKLVFDVHLVEDVHG RQZ[3\WKRQEXWWKHPDLQGLVWLQFWLRQVEHWZHHQDZLQGRZREMHFWDQGDFRQWUROREMHFW ZLOOJHQHUDOO\DSSO\WRERWKRIWKHWRRONLWVZHZLOOEHXVLQJLQWKLVVHFWLRQ

Basic GUI display structure 6WUXFWXUDOO\D*8,LVW\SLFDOO\RUJDQL]HGDVDKLHUDUFK\RIJUDSKLFDOFRPSRQHQWV7KHUH LVDSDUHQWRUEDVHREMHFWZKLFKLVW\SLFDOO\DIUDPHRIVRPHW\SH VHH)LJXUH  7KLVREMHFWLVXVHGDVWKHDQFKRURUSDUHQWIRUVXEVHTXHQW*8,REMHFWVZKLFKWKHP VHOYHVPD\EHSDUHQWVDQGVRRQDVVKRZQLQ)LJXUH7KLVLVQRWDQLQKHULWDQFH VLWXDWLRQEXWUDWKHUDKLHUDUFK\RIIXQFWLRQDOUHODWLRQVKLSV

Graphical User Interfaces | 527 )LJXUH*8,REMHFWKLHUDUFK\ ,QWKHWUHHGLDJUDPVKRZQLQ)LJXUHHDFKFKLOGKDVRQHSDUHQWDQGVRPHQXPEHU RIFKLOGUHQ SRVVLEO\]HUR LWVHOI:KHQZLGJHWVDUHERXQGWRDZLQGRZREMHFWOLNHD IUDPHWKH\FDQEHPDQDJHGDVDJURXSE\WKHSDUHQWREMHFW

GUI functionality )XQFWLRQDOO\D*8,LVEDVHGRQDVWLPXOXVDQGUHVSRQVHSDUDGLJP$VWLPXOXVLVUH IHUUHGWRDVDQHYHQWDQGHYHQWVPD\FRPHIURPDXVHUIURPDQLQWHUQDOSURFHVVRU IURPWKHZRUOGRXWVLGHRIWKHVRIWZDUH:KHQDXVHULQSXWVDFRPPDQGSHUKDSVXVLQJ DPRXVHWRFOLFNRQDEXWWRQRUSUHVVLQJDNH\DQHYHQWLVJHQHUDWHGDQGWKH*8, UHVSRQGVLQVRPHIDVKLRQ:KLOHZDLWLQJIRUDFRPPDQGD*8,PLJKWEHUHFHLYLQJ H[WHUQDOHYHQWVDQGXSGDWLQJGDWDGLVSOD\VRUHYHQJHQHUDWLQJLQWHUQDOHYHQWVYLDWLPHUV RURWKHUPHWKRGV7KLVLVZKDWLVNQRZQDVHYHQWGULYHQSURJUDPPLQJDQGLWLVWKH SUHYDLOLQJPRGHOXVHGLQPRGHUQ*8,V (YHQWVDUHSURFHVVHGE\HYHQWKDQGOHUVDVVLJQHGWRHYHQWVRXUFHV7KLVDVVLJQPHQWLV FDOOHGELQGLQJDQGDQHYHQWKDQGOHULVVDLGWREHERXQGWRDSDUWLFXODUHYHQW$QHYHQW KDQGOHUPD\EHVHOIFRQWDLQHGRULWPD\FDOORWKHUIXQFWLRQVRUPHWKRGVWRSHUIRUP

528 | Chapter 13:ಗUser Interfaces VRPHVSHFLILFSURFHVVLQJ6RPHHYHQWKDQGOHUVDUHEXLOWLQWRWKH*8,IUDPHZRUN7KHVH LQFOXGHQRWLILFDWLRQVRIFKDQJHVWRWKHGLVSOD\VXFKDVZKHQWKHXVHUPRYHVRQHZLQ GRZRQWRSRIDQRWKHURUZKHQDOORSHQZLQGRZVDUHFORVHGDWRQFH %XWIRUWKHPRVWSDUWWKHHYHQWVWKDWZHZLOOEHFRQFHUQHGZLWKDUHWKRVHWKDWDUH JHQHUDWHGE\WKHYDULRXVZLGJHWVLQD3\WKRQ*8,(DFKEXWWRQWH[WILHOGVOLGHUEDU DQGVRRQPXVWKDYHDQHYHQWKDQGOHUDVVRFLDWHGZLWKLW,Q7N,QWHUWKHVHDUHRIWHQ UHIHUUHGWRDV£FDOOEDFNV¤DQGLQZ[3\WKRQWKH\DUHNQRZQDV£HYHQWKDQGOHUV¤EXW WKHQHWHIIHFWLVWKHVDPH %HFDXVHD*8,LVHYHQWGULYHQWKHGLVSOD\PD\VXVSHQGXQWLODQHYHQWRIVRPHVRUWLV UHFHLYHG&RQVLGHUDVLWXDWLRQZKHUHZHPLJKWZDQWD*8,WRGLVSOD\GDWDIURPDQ LQVWUXPHQWHYHU\VHFRQGRUVR8QOHVVWKHUHLVVRPHZD\WRJHQHUDWHDQHYHQWWKDWZLOO FDOOWKHIXQFWLRQWRTXHU\WKHLQVWUXPHQWIRUQHZGDWDDQGWKHQXSGDWHWKHGLVSOD\WKH GLVSOD\ZLOOVLWLGOHXQWLOWKHXVHUFOLFNVRQDQ£8SGDWH¤EXWWRQWRVSHFLILFDOO\UHTXHVW QHZGDWD7KLVPD\VRXQGREYLRXVEXWPDQ\QHRSK\WH*8,SURJUDPPHUVKDYHEHHQ VHHQVLWWLQJDQGVWDULQJDWWKHLUSUHWW\FUHDWLRQVZRQGHULQJKRZWRDYRLGKDYLQJWR UHSHDWHGO\FOLFNDEXWWRQWRVHHWKLQJVFKDQJH7KHVROXWLRQLVWRXVHVRPHNLQGRI LQWHUQDOWLPHUWKDWFDQJHQHUDWHDQHYHQWWRREWDLQQHZGDWDIURPDQH[WHUQDOVRXUFH VXFKDVDQLQVWUXPHQW Z[3\WKRQLQFOXGHVWLPHUIXQFWLRQVDVSDUWRILWVGHVLJQDQGWKHVHDUHUHODWLYHO\VWUDLJKW IRUZDUGWRXVH7N,QWHUXVHVDGLIIHUHQWDSSURDFKZKHUHLQDZLGJHWLWVHOIJHQHUDWHVDQ HYHQWDQGLQYRNHVDFDOOEDFNDIWHUVRPHDPRXQWRIWLPHKDVHODSVHG7KLVLVQ¦WTXLWHDV LQWXLWLYHEXWLWZRUNVILQHZKHQXVHGDSSURSULDWHO\

The GUI main loop ,QD*8,WKHH[HFXWLRQLVGULYHQE\DPDLQORRSDVVKRZQLQ)LJXUHZKLFKLV VLPLODUWRZKDWZHVDZLQWKH$16,WH[WEDVHGLQWHUIDFHVHDUOLHU7KHPDLQORRSLV UHVSRQVLEOHIRUVWDUWLQJWKHLQWHUIDFHKDQGOLQJHYHQWVDQGPHVVDJHVDQGWKHQVKXWWLQJ LWDOOGRZQZKHQLW¦VWLPHWROHDYHDPRQJRWKHUWKLQJV 7KH mainloop() PHWKRG LV D IXQGDPHQWDO DQG QHFHVVDU\ SDUW RI ERWK 7N,QWHU DQG Z[3\WKRQ*8,DSSOLFDWLRQV+RZWKHVHWZRWRRONLWVLPSOHPHQWWKHPDLQORRSGLIIHUV LQWKHGHWDLOVEXWWKHQHWHIIHFWLVWKHVDPH2QFHWKHPDLQORRSVWDUWVLWZLOOQRWUHWXUQ XQWLOWKH*8,VKXWVGRZQDQGH[LWV

TkInter 3\WKRQFRPHVZLWKWKH7N,QWHU*8,WRRONLWDVSDUWRIWKHVWDQGDUGGLVWULEXWLRQ7N,QWHU UHIHUVWRWKH7NSDUWRIWKHWFO7NODQJXDJH WFODQG7NDUHXVXDOO\UHIHUUHGWRDVRQH WKLQJ 7KH7NZLGJHWVHWLVUHODWLYHO\HDV\WRXVHDQGLW¦VYHU\PDWXUHKDYLQJEHHQ DURXQGQRZIRUWKHEHWWHUSDUWRI\HDUV

Graphical User Interfaces | 529 )LJXUH*8,PDLQORRS Planning your GUI %HIRUHHPEDUNLQJRQD*8,SURMHFWRQHRIWKHILUVWVWHSVLVWRGHFLGHZKDWWKHLQWHUIDFH ZLOOORRNOLNH,PSOHPHQWLQJD*8,LQYROYHVPDNLQJGHFLVLRQVDERXWWKLQJVVXFKDV ZKDWZLGJHWVWRXVHKRZWKHSRVLWLRQLQJRIWKHZLGJHWVZLOOEHPDQDJHGWKHUHTXLUHG IXQFWLRQDOLW\ ZKDWZLOOKDSSHQZKHQWKHXVHULQWHUDFWVZLWKDZLGJHW DQGWKHXVHRI LQWHUQDOHYHQWJHQHUDWRUVLIQHFHVVDU\$Q\*8,ZLWKHYHQDPRGHVWDPRXQWRIFRP SOH[LW\LVQ¦WVRPHWKLQJRQHFDQMXVWWKURZWRJHWKHUDQGH[SHFWWRZRUNULJKWRIIWKH EDW,WSD\VWRWDNHWKHWLPHWRWKLQNLWWKURXJKEHIRUHVWDUWLQJ :KHQSODQQLQJD*8,OD\RXW,OLNHWRFUHDWHDGUDZLQJRIZKDW,WKLQNWKH*8,ZLOO ORRNOLNHDQGWKHQXVHWKDWDVDWHPSODWHWRZRUNIURP

Geometry management 7N,QWHUSURYLGHVWKUHHIRUPVRIZLGJHWSODFHPHQWFRQWURODOVRNQRZQDVJHRPHWU\ PDQDJHPHQW7KHVHDUHWKHSDFNJULGDQGSODFHWHFKQLTXHV2IWKHVHWKHSDFNPHWKRG LVWKHVLPSOHVWDQGSUREDEO\WKHPRVWFRPPRQO\XVHG:LWKSDFNZLGJHWVDUHDGGHG IURPWKHRXWVLGHHGJHVRIWKHZLQGRZWRZDUGWKHFHQWHULQHIIHFWSDFNLQJWKHPDVFORVH WRZDUGWKHFHQWHURIWKHZLQGRZVSDFHDVSRVVLEOH :KHQDZLGJHWLVSDFNHGLWZLOOEHSRVLWLRQHGRQWKHVFUHHQVRWKDWLWLVSDFNHGQH[W WRHLWKHUDZLQGRZERUGHURUDQRWKHUZLGJHWZLWKDGGLWLRQDOZLGJHWVVHWQH[WWRLWDQG VRRQ7KHSDFNJHRPHWU\PDQDJHUZLOODWWHPSWWRDUUDQJHWKHZLGJHWVVRDVWRRFFXS\ WKHVPDOOHVWDPRXQWRIVSDFHLQDZLQGRZ7KHELJJHVWGUDZEDFNZLWKSDFNLVWKDWLW GRHVQ¦WJLYH\RXDORWRIGLUHFWFRQWURORYHUZKHUHWKHZLGJHWVZLOOHQGXSDOWKRXJK

530 | Chapter 13:ಗUser Interfaces \RXFDQSXVKWKHPDURXQGE\LQVHUWLQJ£SDGGLQJZLGJHWV¤ LHHPSW\SDQHOVDQG VXFK ,IWKHWRSOHYHOIUDPHLVUHVL]HGWKLQJVFDQVRPHWLPHVJHWVFUDPEOHG 7KHSODFHJHRPHWU\PDQDJHUXVHVDEVROXWH[DQG\SL[HOFRRUGLQDWHVWRSODFHZLGJHWV LQDZLQGRZ:LWKSODFH\RXFDQFUHDWHVRPHYHU\DWWUDFWLYH*8,VEXW\RXUHDOO\QHHG DOD\RXWWRROWRKHOSZLWKWKHSODFHPHQWXQOHVV\RXUZLQGRZLVVPDOODQGVLPSOH 7KHSODFHPHQWPHWKRG,SUHIHUIRUD7N,QWHU*8,LVWKHJULGPHWKRG7KHJULGJHRPHWU\ PDQDJHURUJDQL]HVWKHEDVHZLQGRZE\RYHUOD\LQJDSODFHPHQWJULG7KHZLGWKDQG KHLJKWRIHDFKFROXPQDQGURZDUHGHWHUPLQHGE\WKHVL]HVRIWKHZLGJHWVWKHPVHOYHV

Simple TkInter example 7KHIROORZLQJLVWKHVRXUFHFRGHIRUDVLPSOH7N,QWHU*8,tkdemo1.py,WXVHVWKHJULG JHRPHWU\PDQDJHUWRSODFHVL[ZLGJHWVLQWZRFROXPQV #!/usr/bin/python # tkdemo1.py # # Demonstration of TkInter and the grid placement method. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly.

from Tkinter import *

class demoGUI(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.grid(sticky=W) self.createWidgets()

def createWidgets(self): # get top-level frame reference top=self.winfo_toplevel() # set the start location in the window manager top.wm_geometry('+50+100')

# set the window title self.master.title("Demo 1")

# configure the global grid behavior self.master.rowconfigure( 0, weight = 1 ) self.master.columnconfigure( 0, weight = 1 ) self.grid(sticky = W+E+N+S)

# create string objects for use with label widgets self.var1 = StringVar() self.var1.set("") self.var2 = StringVar() self.var2.set("")

# output state toggle flags self.toggle1 = 0

Graphical User Interfaces | 531 self.toggle2 = 0

# create three buttons and three label widgets, one of which # is a dummy placeholder (for now)

# bind buttons 1 and 2 to event handlers

# the two active label widgets will display green text on a # black background

self.button1 = Button(self, text="Button 1", width=10) self.button1.grid(row=0, column=0) self.button1.bind("", self.button1_Click)

self.text1 = Label(self, text="", width=10, relief=SUNKEN, bg="black", fg="green", textvariable=self.var1) self.text1.grid(row=0, column=10)

self.button2 = Button(self, text="Button 2", width=10) self.button2.grid(row=1, column=0) self.button2.bind("", self.button2_Click)

self.text2 = Label(self, text="", width=10, relief=SUNKEN, bg="black", fg="green", textvariable=self.var2) self.text2.grid(row=1, column=10)

self.button3 = Button(self, text="Quit", width=10, command=self.quit) self.button3.grid(row=2, column=0)

# dummy space filler # you could modify this to display something self.text3 = Label(self, text="", width=10) self.text3.grid(row=2, column=10)

def button1_Click(self, event): if self.toggle1 == 0: self.var1.set("0000") self.toggle1 = 1 else: self.var1.set("1111") self.toggle1 = 0

print "Button 1"

def button2_Click(self, event): if self.toggle2 == 0: self.var2.set("0000") self.toggle2 = 1 else: self.var2.set("1111") self.toggle2 = 0

532 | Chapter 13:ಗUser Interfaces print "Button 2"

app = demoGUI() app.mainloop() 2QD:LQGRZVV\VWHPWKHtkdemo1.pyH[DPSOHZLOOFUHDWHDVLPSOHGLDORJOLNHWKHRQH VKRZQLQ)LJXUH,WORRNVPXFKWKHVDPHRQD/LQX[PDFKLQHH[FHSWIRUWKH VW\OLVWLFGLIIHUHQFHVEHWZHHQWKHZLQGRZPDQDJHUV

)LJXUH7N,QWHU*8,H[DPSOH

,QWKHIROORZLQJH[DPSOHtkdemo2.py,HOHFWHGWRXVHWKHODEHOZLGJHWVIRUWH[WRXWSXW RQFHDJDLQPDLQO\EHFDXVHLW¦VHDV\7REHKRQHVW7N¦V7H[WZLGJHWLVVRPHWKLQJRID SDLQWRXVHVR,WHQGWRDYRLGLW+HUH¦VWKHVRXUFHFRGHIRUtkdemo2.py #!/usr/bin/python # tkdemo2.py # # 2nd Demonstration of TkInter and the grid placement method. # # Source code from the book "Real World Instrumentation with Python" # By J. M. Hughes, published by O'Reilly. from Tkinter import * import time

class demoGUI(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.createWidgets()

def createWidgets(self): # get top-level frame reference top=self.winfo_toplevel() # set the start location in the window manager top.wm_geometry('+50+100')

# set the window title self.master.title("Demo 2")

# create string objects for use with label widgets self.var1 = StringVar() self.var1.set("") self.var2 = StringVar() self.var2.set("")

self.master.rowconfigure(0, weight = 1)

Graphical User Interfaces | 533 self.master.columnconfigure(0, weight = 1) self.grid(sticky = W+E+N+S)

self.text1 = Label(self, text="", width = 15, height = 4, relief=RAISED, bg="white", fg="black", textvariable=self.var1) self.text1.grid(rowspan = 2, sticky = W+E+N+S)

self.button1 = Button(self, text = "RUN", width = 10, height = 2) self.button1.grid(row = 0, column = 1, sticky = W+E+N+S) self.button1.bind("", self.button1_Click)

self.button2 = Button(self, text = "STOP", width = 10, height = 2) self.button2.grid(row = 0, column = 2, sticky = W+E+N+S) self.button2.bind("", self.button2_Click)

self.button3 = Button(self, text = "Test", width = 10, height = 2) self.button3.grid(row = 1, column = 1,sticky = W+E+N+S) self.button3.bind("", self.button3_Click)

self.button4 = Button(self, text = "Reset", width = 10, height = 2) self.button4.grid(row = 1, column = 2, sticky = W+E+N+S) self.button4.bind("", self.button4_Click)

self.entry = Entry(self, relief=RAISED) self.entry.grid(row = 2, columnspan = 2, sticky = W+E+N+S) self.entry.insert(INSERT, "Command")

self.text2 = Label(self, text="Stopped", width = 2, height = 2, relief=RAISED, bg="white", fg="black", textvariable=self.var2) self.text2.grid(row = 2, column = 2, sticky = W+E+N+S)

self.rowconfigure(1, weight = 1) self.columnconfigure(1, weight = 1)

def button1_Click(self, event): self.var1.set("") self.var2.set("Running")

def button2_Click(self, event): self.var1.set("") self.var2.set("Stopped")

def button3_Click(self, event): time.sleep(1) self.var1.set("Test OK") self.var2.set("Stopped")

def button4_Click(self, event): time.sleep(1) self.var1.set("Reset OK") self.var2.set("Stopped")

534 | Chapter 13:ಗUser Interfaces app = demoGUI() app.mainloop() 7KHRXWSXWRItkdemo2.pyLVVKRZQLQ)LJXUH

)LJXUHWNGHPRH[DPSOH*8,

7KHUHDUHDIHZWKLQJVWRQRWHLQtkdemo2.py7KHILUVWLVKRZWKHJULGJHRPHWU\PDQDJHU LVXVHGWRRUJDQL]HZLGJHWVRIGLIIHUHQWVL]HV7KHVHFRQGLVKRZWKHEXWWRQVKDYHDQ HIIHFWRQPRUHWKDQMXVWRQHODEHOZLGJHW)LQDOO\\RXPLJKWKDYHQRWLFHGWKDWZKHQ HLWKHUWKH7HVWRUWKH5HVHWEXWWRQLVFOLFNHGWKH*8,IUHH]HVEULHIO\7KLVLVGXHWRWKH time.sleep(1)FDOOLQWKHHYHQWKDQGOHUVIRUWKHVHEXWWRQV,WDOVRVKRZVZK\D*8, VKRXOGRQO\VSHQGDVPXFKWLPHVHUYLFLQJDQLQSXWHYHQWDVQHFHVVDU\DQGQRPRUH

Tools and resources for TkInter 7KHUHDUHVHYHUDOJRRGOD\RXWWRROVDYDLODEOHIRU7N,QWHU*RRJOHLVRIFRXUVHRQHSODFH WRVWDUWORRNLQJ6RXUFH)RUJH KWWSZZZVRXUFHIRUJHQHW LVDQRWKHU6LQFH,WHQGWR EXLOG7N,QWHU*8,VE\KDQG,GRQ¦WFXUUHQWO\KDYHDSHUVRQDOIDYRULWH1HYHUWKHOHVV KHUHDUHVRPHOLQNVWRDFRXSOHRI7N,QWHU*8,FRQVWUXFWLRQWRROVWKDW\RXPD\ILQG XVHIXO 3$*( KWWSSDJHVRXUFHIRUJHQHW 7KH3$*(WRROLVLQWHQGHGWREHXVHGWRFUHDWHVLQJOHZLQGRZVLWLVQRWDIXOO DSSOLFDWLRQFRQVWUXFWLRQWRRO,W¦VDVROLGWRROWKDWFDQKHOSDOOHYLDWHDORWRIWKH WHGLXPRIFUHDWLQJFRPSOH[ZLQGRZVZLWKPXOWLSOHZLGJHWV%HVXUHWRUHDGWKH LQWURGXFWRU\GRFXPHQWDWLRQVRWKDW\RXIXOO\XQGHUVWDQGWKHLQWHQWDQGOLPLWDWLRQV RI3$*( 6SHF7FO KWWSVSHFWFOVRXUFHIRUJHQHW 7KLVKDVQ¦WEHHQXSGDWHGLQDZKLOHDQGLWPD\KDYHDIHZTXLUNVEXWWKHODVWWLPH ,ZRUNHGZLWKLW,OLNHGLW,I\RX¦UHIHHOLQJEUDYHLWPD\EHZRUWKDORRN )RUOHDUQLQJDQGUHIHUHQFHUHVRXUFHVIRU7N,QWHUEHVXUHWRFKHFNWKHVHFWLRQ£6XJJHV WHG5HDGLQJ¤RQSDJH wxPython Z[3\WKRQLVWKHQHZNLGRQWKH3\WKRQ*8,EORFNEXWLWVXQGHUO\LQJOLEUDU\KDVEHHQ DURXQGIRUDZKLOH,WLVDFWXDOO\DZUDSSHUDURXQGWKHZ[:LGJHWVWRRONLWZKLFKLV

Graphical User Interfaces | 535 ZULWWHQLQ&2QHRIWKHSULPDU\REMHFWLYHVRIZ[:LGJHWVLVDFKLHYLQJSRUWDELOLW\ ZKLOHPDLQWDLQLQJWKHORRNDQGIHHORIWKHKRVWRSHUDWLQJV\VWHP :KHUHDVZLWK7N,QWHU,WHQGWRMXVWEXLOGDVLPSOH*8,E\KDQGXVLQJWKHJULGJHRPHWU\ PDQDJHUZLWKZ[3\WKRQ,SUHIHUWRXVHDWRRODQGVSHFLI\H[DFWZLGJHWORFDWLRQVXVLQJ SL[HOFRRUGLQDWHV)RUWKLV,XVHWKH%RD&RQVWUXFWRUZ[3\WKRQ*8,EXLOGHUZKLFK DOVRLQFRUSRUDWHVDGHFHQWGHEXJJHUDQGDVHUYLFHDEOHWH[WHGLWRU

Designing a wxPython GUI $V,VWDWHGHDUOLHUIRU7N,QWHULW¦VDJRRGLGHDWRKDYHDSODQIRU\RXU*8,$OWKRXJKD YLVXDOWRROOLNH%RDPDNHVLWHDV\WRWU\RXWGLIIHUHQWZLGJHWDUUDQJHPHQWVLW¦VDOVRHDV\ WRJHWORVWLQWKHGHWDLOV.HHSLWVLPSOH 2QFH\RX¦YHGHFLGHGRQWKHJHQHUDODSSHDUDQFHRI\RXU*8,WKHQH[WVWHSLVWRVWDUW XS%RD DVVXPLQJWKDW\RXKDYHDOUHDG\LQVWDOOHGLWRIFRXUVH $IWHULWLQLWLDOL]HV\RX¦OO VHHWKUHHZLQGRZVDVVKRZQLQ)LJXUH

Building a simple wxPython GUI %RD KDV WKH DELOLW\ WR DVVLVW ZLWK WKH FUHDWLRQ RI WKH HQWLUH *8, IURP D WRSOHYHO wx.appREMHFWWKURXJKWRWKHYDULRXVIUDPHVDQGGLDORJVLWPLJKWQHHG+RZHYHULW¦V VWLOOXSWR\RXWRDVVHPEOHLWDOOLQWRDZRUNLQJDSSOLFDWLRQ:KDWZH¦OOORRNDWKHUHLV DVLPSOHVLQJOHIUDPHGLDORJW\SH*8,VLPLODUWRZKDWZH¦YHDOUHDG\EXLOWXVLQJ7N,QWHU 7KLV*8,ZLOOKDYHVRPHGDWDGLVSOD\ILHOGVVRPHEXWWRQVDQGVRPHELWPDSVWDWXV LQGLFDWRUV,W¦VDFWXDOO\D*8,YDULDWLRQRQZKDWZHVDZHDUOLHULQWKLVFKDSWHUZLWKWKH $16,GDWDGLVSOD\V :LWK%RDUXQQLQJDQGUHDG\WKHILUVWVWHSLVWRFUHDWHDQHZGHVLJQ,QWKHWRSZLQGRZ VHOHFW WKH £1HZ¤ WDE LW VKRXOG DOUHDG\ EH VHOHFWHG  7KHQ VHOHFW WKH VL[WK EXWWRQ wx.frame7KHPDLQZLQGRZVKRXOGQRZFRQWDLQVRPH3\WKRQFRGH7KLVLVWKHVNHOHWRQ RQZKLFKZH¦OOEXLOGRXUDSSOLFDWLRQ 1RZFRPHVWKHIXQSDUW6HOHFWWKHLFRQZLWKWKHEOXHDUURZLQDER[LQWKHWRROEDUMXVW DERYHWKHFRGHHGLWRUGLVSOD\

536 | Chapter 13:ಗUser Interfaces )LJXUH%RD&RQVWUXFWRUXVHULQWHUIDFH

)LJXUH%RD¦VZLGJHWOD\RXWZLQGRZ

Graphical User Interfaces | 537 ,¦PJRLQJWRIDVWIRUZDUGWKLQJVKHUHEHFDXVH,ZDQWWRVKRZ\RXZKDW\RXFDQGR ZLWK%RDEXW,GRQ¦WZDQWWRJRWKURXJKDVWHSE\VWHSWXWRULDO WKDWZRXOGUHTXLUHD ERRNRILWVRZQ ,QVWHDGZKDW,¦YHGRQHLVVHOHFWYDULRXVZLGJHWVIURPWKHWRROEDULQ WKHXSSHUPRVWZLQGRZDQGWKHQGURSSHGWKHPRQWRWKHOD\RXWDUHD,¦YHDOVRHGLWHG YDULRXVGDWDLWHPVXVLQJWKHSDQHOVLQWKHOHIWPRVWZLQGRZ7KHVHDOORZ\RXWRGR WKLQJVOLNHVHWWKHIUDPHWLWOHFKDQJHIRQWVDQGFRORUVGHILQHVL]HVDQGVHWWKHYDULRXV VWDWLFWH[WVWULQJVXVHGIRUEXWWRQVDQGVWDWLFWH[WZLGJHWV )LJXUHVKRZVWKHHQGUHVXOWDIWHUDIHZPLQXWHV¦ZRUWKRIZRUN

)LJXUH)LQLVKHGOD\RXW

7RFUHDWHWKLVIUDPH,ILUVWOLJKWHQHGWKHEDFNJURXQGFRORUDQGWKHQSODFHGWKHYDULRXV ZLGJHWV,WKHQDVVLJQHGDSSURSULDWHQDPHVDQGJHQHUDWHGWKHHYHQWKDQGOHUVNHOHWRQV IRUWKHVL[EXWWRQV,QFDVH\RX¦UHFXULRXVWKHIRXUEXWWRQVZLWKWKHGLDJRQDOOLQHVDUH ELWPDSEXWWRQV,QRWKHUZRUGVDELWPDSFDQEHDSSOLHGWRWKHPG\QDPLFDOO\ZKLFK ,ZLOOGRLQWKHQH[WVWHS:KHQ,FOLFNRQWKHEOXHFKHFNPDUNDWWKHWRSRIWKHHGLWRU ZLQGRZ%RDFRQYHUWVWKHGHVLJQLQWRFRGHZKLFKLVVKRZQKHUH #Boa:Frame:Frame1

import wx import wx.lib.buttons

def create(parent): return Frame1(parent)

[wxID_FRAME1, wxID_FRAME1BITMAPBUTTON1, wxID_FRAME1BITMAPBUTTON2, wxID_FRAME1BITMAPBUTTON3, wxID_FRAME1BITMAPBUTTON4, wxID_FRAME1EXITBUTTON, wxID_FRAME1STATICTEXT1, wxID_FRAME1STATICTEXT2, wxID_FRAME1STATICTEXT3, wxID_FRAME1STATICTEXT4, wxID_FRAME1STATICTEXT5, wxID_FRAME1TEXTCTRL1, wxID_FRAME1TEXTCTRL2, wxID_FRAME1TEXTCTRL3, wxID_FRAME1TEXTCTRL4, wxID_FRAME1UPDTBUTTON, ] = [wx.NewId() for _init_ctrls in range(16)]

538 | Chapter 13:ಗUser Interfaces class Frame1(wx.Frame): def _init_ctrls(self, prnt): # generated method, don't edit wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt, pos=wx.Point(331, 258), size=wx.Size(400, 250), style=wx.DEFAULT_FRAME_STYLE, title='wxPython Demo') self.SetClientSize(wx.Size(392, 223)) self.SetBackgroundColour(wx.Colour(187, 187, 187)) self.SetToolTipString('')

self.staticText1 = wx.StaticText(id=wxID_FRAME1STATICTEXT1, label='Channel 1', name='staticText1', parent=self, pos=wx.Point(24, 64), size=wx.Size(61, 14), style=0) self.staticText1.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Tahoma'))

self.staticText2 = wx.StaticText(id=wxID_FRAME1STATICTEXT2, label='Channel 2', name='staticText2', parent=self, pos=wx.Point(24, 96), size=wx.Size(61, 14), style=0) self.staticText2.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Tahoma'))

self.staticText3 = wx.StaticText(id=wxID_FRAME1STATICTEXT3, label='Channel 3', name='staticText3', parent=self, pos=wx.Point(24, 128), size=wx.Size(61, 14), style=0) self.staticText3.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Tahoma'))

self.staticText4 = wx.StaticText(id=wxID_FRAME1STATICTEXT4, label='Channel 4', name='staticText4', parent=self, pos=wx.Point(24, 160), size=wx.Size(61, 14), style=0) self.staticText4.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD, False, 'Tahoma'))

self.textCtrl1 = wx.TextCtrl(id=wxID_FRAME1TEXTCTRL1, name='textCtrl1', parent=self, pos=wx.Point(104, 56), size=wx.Size(100, 21), style=0, value='') self.textCtrl1.SetEditable(False) self.textCtrl1.SetToolTipString('')

self.textCtrl2 = wx.TextCtrl(id=wxID_FRAME1TEXTCTRL2, name='textCtrl2', parent=self, pos=wx.Point(104, 88), size=wx.Size(100, 21), style=0, value='') self.textCtrl2.SetEditable(False) self.textCtrl2.SetToolTipString('')

self.textCtrl3 = wx.TextCtrl(id=wxID_FRAME1TEXTCTRL3, name='textCtrl3', parent=self, pos=wx.Point(104, 120), size=wx.Size(100, 21), style=0, value='') self.textCtrl3.SetEditable(False) self.textCtrl3.SetToolTipString('')

self.textCtrl4 = wx.TextCtrl(id=wxID_FRAME1TEXTCTRL4, name='textCtrl4', parent=self, pos=wx.Point(104, 152), size=wx.Size(100, 21), style=0, value='')

Graphical User Interfaces | 539 self.textCtrl4.SetEditable(False) self.textCtrl4.SetToolTipString('')

self.bitmapButton1 = wx.BitmapButton(bitmap=wx.NullBitmap, id=wxID_FRAME1BITMAPBUTTON1, name='bitmapButton1', parent=self, pos=wx.Point(224, 56), size=wx.Size(24, 24), style=wx.BU_AUTODRAW) self.bitmapButton1.SetToolTipString('') self.bitmapButton1.Bind(wx.EVT_BUTTON, self.OnBitmapButton1Button, id=wxID_FRAME1BITMAPBUTTON1)

self.bitmapButton2 = wx.BitmapButton(bitmap=wx.NullBitmap, id=wxID_FRAME1BITMAPBUTTON2, name='bitmapButton2', parent=self, pos=wx.Point(224, 88), size=wx.Size(24, 24), style=wx.BU_AUTODRAW) self.bitmapButton2.SetToolTipString('') self.bitmapButton2.Bind(wx.EVT_BUTTON, self.OnBitmapButton2Button, id=wxID_FRAME1BITMAPBUTTON2)

self.bitmapButton3 = wx.BitmapButton(bitmap=wx.NullBitmap, id=wxID_FRAME1BITMAPBUTTON3, name='bitmapButton3', parent=self, pos=wx.Point(224, 120), size=wx.Size(24, 24), style=wx.BU_AUTODRAW) self.bitmapButton3.SetToolTipString('') self.bitmapButton3.Bind(wx.EVT_BUTTON, self.OnBitmapButton3Button, id=wxID_FRAME1BITMAPBUTTON3)

self.bitmapButton4 = wx.BitmapButton(bitmap=wx.NullBitmap, id=wxID_FRAME1BITMAPBUTTON4, name='bitmapButton4', parent=self, pos=wx.Point(224, 152), size=wx.Size(24, 24), style=wx.BU_AUTODRAW) self.bitmapButton4.SetToolTipString('') self.bitmapButton4.Bind(wx.EVT_BUTTON, self.OnBitmapButton4Button, id=wxID_FRAME1BITMAPBUTTON4)

self.updtButton = wx.lib.buttons.GenButton(id=wxID_FRAME1UPDTBUTTON, label='Update', name='updtButton', parent=self, pos=wx.Point(304, 56), size=wx.Size(76, 65), style=0) self.updtButton.SetToolTipString('Fetch fresh data') self.updtButton.Bind(wx.EVT_BUTTON, self.OnUpdtButtonButton, id=wxID_FRAME1UPDTBUTTON)

self.exitButton = wx.lib.buttons.GenButton(id=wxID_FRAME1EXITBUTTON, label='Exit', name='exitButton', parent=self, pos=wx.Point(304, 192), size=wx.Size(76, 25), style=0) self.exitButton.Bind(wx.EVT_BUTTON, self.OnExitButtonButton, id=wxID_FRAME1EXITBUTTON)

self.staticText5 = wx.StaticText(id=wxID_FRAME1STATICTEXT5, label='Enable/Disable', name='staticText5', parent=self, pos=wx.Point(200, 32), size=wx.Size(70, 13), style=0)

def __init__(self, parent): self._init_ctrls(parent)

540 | Chapter 13:ಗUser Interfaces def OnBitmapButton1Button(self, event): event.Skip()

def OnBitmapButton2Button(self, event): event.Skip()

def OnBitmapButton3Button(self, event): event.Skip()

def OnBitmapButton4Button(self, event): event.Skip()

def OnUpdtButtonButton(self, event): event.Skip()

def OnExitButtonButton(self, event): event.Skip() (YHQDVMXVWDVNHOHWRQWKHUHLVVWLOORIORWRIFRGHWKHUH)RUWXQDWHO\ZHZRQ¦WUHDOO\ KDYHWRZRUU\DERXWDQ\WKLQJDERYHWKH__init__()PHWKRG:KDWZH¦UHLQWHUHVWHGLQ QRZDUHWKHVL[EXWWRQHYHQWKDQGOHUV 6LQFHWKH([LWEXWWRQGRHVQ¦WGRDQ\WKLQJ\HWOHW¦VDGGWKHFRGHVRLWZLOODFWXDOO\H[LW def OnExitButtonButton(self, event): self.Destroy() ,I\RXUDSSOLFDWLRQQHHGVWRGRVRPHKRXVHNHHSLQJEHIRUHVKXWWLQJGRZQWKLVLVRQH SODFHWRGRLW,QRXUFRGHZH¦OOVLPSO\FDOOWKHDestroy()PHWKRGDQGGURSLWRQWKH IORRU 1H[WXSDUHWKHIRXU(QDEOH'LVDEOHEXWWRQV)RUWKHVH,¦PJRLQJWRXVHVPDOOELWPDS LPDJHVWRLQGLFDWHWKHRQRIIVWDWXV7KHLPDJHVDUHVZDSSHGRXWE\WKHHYHQWKDQGOHUV $QHZPHWKRGinitButtons()ZLOOEHXVHGWRVHWWKHLQLWLDOVWDWHRIIRXUWUDFNLQJYDU LDEOHVDQGDVVLJQDUHGELWPDSWRHDFKRIWKHEXWWRQV def initButtons(self): self.cnt = 1

self.btn1State = Flase self.btn2State = Flase self.btn3State = Flase self.btn4State = Flase

self.bitmapButton1.SetBitmapLabel(bitmap= wx.Bitmap(u'red24off.bmp',wx.BITMAP_TYPE_BMP)) self.bitmapButton2.SetBitmapLabel(bitmap= wx.Bitmap(u'red24off.bmp',wx.BITMAP_TYPE_BMP)) self.bitmapButton3.SetBitmapLabel(bitmap= wx.Bitmap(u'red24off.bmp',wx.BITMAP_TYPE_BMP)) self.bitmapButton4.SetBitmapLabel(bitmap= wx.Bitmap(u'red24off.bmp',wx.BITMAP_TYPE_BMP))

Graphical User Interfaces | 541 7KHYDULDEOHself.cntLVXVHGE\WKH8SGDWHEXWWRQinitButtons()LVFDOOHGIURPZLWKLQ __init__()DIWHUWKHFRQWUROVKDYHEHHQLQLWLDOL]HG def __init__(self, parent): self._init_ctrls(parent) self.initButtons() +HUHLVWKHHYHQWKDQGOHUIRUWKHILUVWEXWWRQ def OnBitmapButton1Button(self, event): if self.btn1State == False: self.btn1State = True self.bitmapButton1.SetBitmapLabel(bitmap= wx.Bitmap(u'green24on.bmp', wx.BITMAP_TYPE_BMP)) else: self.btn1State = False self.bitmapButton1.SetBitmapLabel(bitmap= wx.Bitmap(u'red24off.bmp', wx.BITMAP_TYPE_BMP)) ([FHSWIRUQDPHFKDQJHVWKHRWKHUWKUHHDUHLGHQWLFDO)LQDOO\WKHHYHQWKDQGOHUIRU WKH8SGDWHEXWWRQZLOOSXVKDVWULQJLQWRHDFKRIWKHIRXUWH[WZLGJHWVHDFKWLPHWKH 8SGDWHEXWWRQLVFOLFNHG def OnUpdtButtonButton(self, event): self.textCtrl1.SetValue(str(self.cnt)) self.textCtrl2.SetValue(str(self.cnt)) self.textCtrl3.SetValue(str(self.cnt)) self.textCtrl4.SetValue(str(self.cnt)) self.cnt += 1 7RJHWLWJRLQJDOOZHQHHGLVWKLVOLWWOHELWRIFRGHDWWKHHQGRIWKHILOH if __name__ == '__main__': app = wx.PySimpleApp() frame = create(None) frame.Show(True) app.MainLoop() 7KLVXVHVZ[3\WKRQ¦Vwx.PySimpleApp()FODVVWRFUHDWHDQDSSREMHFWWRSURYLGHWKH PDLQORRS:KHQwxexample.pyLVUXQWKHUHVXOWLQJGLVSOD\ORRNVOLNH)LJXUH 7KHUHZHUHDORWRIGHWDLOVLQWKHFUHDWLRQRIwxexample.pyWKDW,GLGQ¦WJRLQWR,I\RX ZDQWWRNQRZPRUHDERXWZ[3\WKRQRU%RD&RQVWUXFWRU,ZRXOGHQFRXUDJH\RXWR DWWHPSWWRGXSOLFDWHWKLVH[DPSOHIRU\RXUVHOI,I\RXZDQWWRORRNDWWKHVHWWLQJVIRU WKHZLGJHWVMXVWORDGWKHVRXUFHLQWR%RDDQGH[DPLQHWKHYDULRXVVHWWLQJVSDQHOV

Tools and resources for wxPython :H¦YHDOUHDG\VHHQKRZDWRROOLNH%RD&RQVWUXFWRUFDQEHXVHGWRFUHDWHDZ[3\WKRQ DSSOLFDWLRQEXWWKHUHDUHRWKHUWRROVDYDLODEOHDVZHOO

542 | Chapter 13:ಗUser Interfaces Z[*ODGH KWWSZ[JODGHVRXUFHIRUJHQHW 0RGHOHGRQWKH*ODGH*8,GHVLJQHUWRROIRUWKH*7.*120(HQYLURQPHQW Z[*ODGHLVQRWDFRPSOHWHDSSOLFDWLRQWRROOLNH%RDEXWLWLVDFDSDEOHWRROIRU EXLOGLQJLQGLYLGXDOZLQGRZVDQGGLDORJVPXFKOLNHWKH3$*(WRROIRU7N,QWHU 3\WKRQ&DUG KWWSS\WKRQFDUGVRXUFHIRUJHQHW ,QWHQGHGDVDWRROIRUTXLFNO\FUHDWLQJVLPSOH*8,DSSOLFDWLRQV3\WKRQ&DUGHP SOR\VWHPSODWHVDQGDEDVLFVHOHFWLRQRIZLGJHWV:LWKLWVIRFXVRQQHZSURJUDP PHUVLWPD\EHDJRRGSODFHWRVWDUW+RZHYHUEHIRUHZDUQHGWKDWWKHFRGHKDVQ¦W EHHQXSGDWHGVLQFHDQGERWK3\WKRQDQGZ[3\WKRQKDYHPRYHGDORQJVLQFH WKHQ6RPHIHDWXUHVPD\QRORQJHUZRUN 7KHUHLVDODUJHDPRXQWRILQIRUPDWLRQDYDLODEOHRQWKH:HEDQGWKHVHFWLRQ£6XJJHVWHG 5HDGLQJ¤RQSDJHOLVWVVRPHHVVHQWLDOVRXUFHVRILQIRUPDWLRQ

)LJXUHZ[H[DPSOHS\

Summary ,QWKLVFKDSWHUZH¦YHORRNHGDWERWKWH[WEDVHGDQGJUDSKLFDOLQWHUIDFHV:H¦YHVHHQ WKDWZKLOHDWH[WEDVHGLQWHUIDFHLVILQHIRUDORWRIDSSOLFDWLRQVWKHUHDUHWLPHVZKHQ FKDUDFWHUVDORQHVLPSO\ZRQ¦WSURYLGHWKHOHYHORIGLVSOD\ILGHOLW\RUWKHLQWHUIDFHIXQF WLRQDOLW\QHHGHGIRUDQDSSOLFDWLRQ 2QHWKLQJ\RXPD\KDYHQRWLFHGDERXWWKHWZRVW\OHVRILQWHUIDFHSURJUDPPLQJLVWKDW WKH\ERWKHPSOR\DPDLQORRSRIVRPHIRUPWRKDQGOHLQSXWRXWSXWDQGXSGDWHIXQF WLRQV7KLVLVSUREDEO\RQHRIWKHROGHVWSDWWHUQVLQFRPSXWHUSURJUDPPLQJDQGLW PDNHVDORWPRUHVHQVHWKDQWU\LQJWRVFDWWHUWKHVFUHHQFRQWUROIXQFWLRQDOLW\DURXQGD SURJUDPDQGWKHQNHHSWUDFNRILWDOO ,¦YHVHHQWKDWGRQHDFWXDOO\DQGDV\RXPLJKW JXHVVLWZDVQ¦WYHU\UREXVWRUHDV\WRPDLQWDLQ 

Summary | 543 &UHDWLQJ7N,QWHUH[DPSOHVE\KDQGDQGDZ[3\WKRQH[DPSOHXVLQJDWRRODOORZHGPH WRLOOXVWUDWHERWKDSSURDFKHVDVZHOODVKLJKOLJKWLQJWKHGLIIHUHQFHVLQHDVHRIXVHEH WZHHQ7N,QWHUDQGZ[3\WKRQWKDWDUHURRWHGLQWKHLUGHVLJQSKLORVRSKLHV,QWKHFDVH RI7N,QWHUPDQ\SURIHVVLRQDOVRIWZDUHGHYHORSHUVGRQ¦WHYHQERWKHUZLWKDWRROEH FDXVH WKH\ FDQ TXLFNO\ FUHDWH IXQFWLRQDO DQG DHVWKHWLFDOO\ SOHDVLQJ LQWHUIDFHV XVLQJ 7N,QWHU¦VQDWLYHJHRPHWU\PDQDJHPHQWFDSDELOLWLHV:KLOHLWLVSHUIHFWO\IHDVLEOHWRWDNH WKHVDPHDSSURDFKZLWKZ[3\WKRQ,FKRVHWRXVHWKH%RD&RQVWUXFWRUWRROWRLOOXVWUDWH WKHVWHSVLQYROYHGLQWKHSURFHVV0RVW*8,WRROVIRUERWK7N,QWHUDQGZ[3\WKRQ DQG RWKHU*8,OLEUDULHV ZLOOZRUNLQVLPLODUZD\VVRWKLVH[DPSOHVKRXOGEHDSSOLFDEOH DFURVVDYDULHW\RIWRROVDQGVLWXDWLRQV ,WLVP\KRSHWKDWZKDW\RX¦YHVHHQKHUHZLOOJLYH\RXVRPHLGHDVIRU\RXURZQDSSOL FDWLRQV$V,VWDWHGVHYHUDOWLPHVLQWKLVFKDSWHULI\RX¦UHJRLQJWRGRDQ\VHULRXVZRUN ZLWKWKLQJVOLNHcursesRUD*8,\RXRZHLWWR\RXUVHOIWRFKHFNRXWWKHERRNVDQG OLQNVLQWKH£6XJJHVWHG5HDGLQJ¤VHFWLRQEHORZ Suggested Reading +HUHDUHVRPHERRNVWKDW,¦YHIRXQGXVHIXOZKHQZRUNLQJZLWKcursesDQGZLWKWKH Z[3\WKRQDQG7N,QWHU*8,WRRONLWV,¦YHDOVRLQFOXGHGVRPHOLQNVWRLQWHUHVWLQJVRXUFHV RILQIRUPDWLRQWKDWKDYHDGLUHFWEHDULQJRQWKLVFKDSWHU 3URJUDPPHU¦V*XLGHWRQFXUVHV'DQ*RRNLQ-RKQ:LOH\ 6RQV 'DQ*RRNLQ¦VERRNFRYHUVWKHncurses9HUVLRQOLEUDU\DQGLVUHOHYDQWWR8QL[ /LQX[DQG0DF26;$OWKRXJKLWRQO\FRYHUVWKH&$3,WKHWXWRULDOPDWHULDOKHOSV WRFODULI\ZKDW3\WKRQ¦VcursesOLEUDU\LVGRLQJDQGZK\DQGPDQ\RIWKHH[DPSOHV DUHHDVLO\WUDQVODWHGLQWR3\WKRQ7KHUHIHUHQFHVHFWLRQVRIWKHERRNDUHDOVRYHU\ XVHIXO DQG SURYLGH D TXLFN DQG HDV\ ZD\ WR ORRN XS REVFXUH RU KDOIIRUJRWWHQ IXQFWLRQV Z[3\WKRQLQ$FWLRQ1RHO5DSSLQDQG5RELQ'XQQ0DQQLQJ3XEOLFDWLRQV &RYHUVWKH3\WKRQ$3,IRUWKHwxWidgetsOLEUDU\,QFOXGHVQXPHURXVH[DPSOHVEXW WKHUH¦VDORWWRFRYHUKHUHVRVRPHWRSLFVJHWDPRUHLQGHSWKWUHDWPHQWWKDQRWKHUV -XVW EH SUHSDUHG WR HLWKHU GLJ LQWR WKH Z[3\WKRQ VRXUFH RU WUDQVODWH IURP WKH &VRXUFHVLQWKHZ[:LGJHWVFRGHLI\RXKDYHDQHHGIRUVRPHDUFDQHNQRZOHGJH RQDWRSLFQRWIXOO\FRYHUHGLQWKHERRN,QDQ\FDVHLI\RXZDQWWRZRUNZLWK Z[3\WKRQ\RXQHHGWRKDYHWKLVERRNRQ\RXUGHVN &URVV3ODWIRUP*8,3URJUDPPLQJZLWK:[:LGJHWV-XOLDQ6PDUW.HYLQ+RFNDQG6WH IDQ&VRPHU3HDUVRQ(GXFDWLRQ3UHQWLFH+DOO 7KHGHILQLWLYHUHIHUHQFHIRUZ[:LGJHWV7KLVERRNLVWKLFNDQGIXOORIGHWDLOVDQG ZKHQZ[3\WKRQGRHVQ¦WPDNHVHQVHWKLVLVZKHUH\RXFDQWXUQWRILQGRXWZKDW VKRXOGEHJRLQJRQ%HIRUHZDUQHGWKDW\RXZLOOQHHGWREHDEOHWRUHDGDQGXQ GHUVWDQGDWOHDVWWKHEDVLFVRI&WRJHWWKHPRVWRXWRIWKLVERRN

544 | Chapter 13:ಗUser Interfaces 3\WKRQDQG7NLQWHU-RKQ(*UD\VRQ0DQQLQJ3XEOLFDWLRQV &RYHUVWKH7N,QWHU*8,WRRONLWLQGHWDLOZLWKQXPHURXVH[DPSOHVDQGGHVFULSWLRQV RIWKHDYDLODEOHZLGJHWV,WGRHVWHQGWRRPLWVRPHGHWDLOVIRUWKHVDNHRIEUHYLW\ DQGWKHUHIHUHQFHVWRWKH3\WKRQ0HJDZLGJHWV pmw DGGRQOLEUDU\DUHQRZRXWRI GDWH ZKLFKLVQRWVXUSULVLQJFRQVLGHULQJWKDWWKHERRNLVQRZ\HDUVROG 7KH %/7PRGXOHXVHGE\pmwLQSDUWLFXODUVHHPVWREHZRHIXOO\RXWRIGDWHDQGODVW WLPH,FKHFNHGLWKDGLVVXHVZLWK3\WKRQ%XWLI\RX¦GUDWKHUXVH7N,QWHUDQG GRQ¦WQHHGWKHIXQFWLRQDOLW\LQ%/7LWVKRXOGQ¦WEHDSUREOHP KWWSDUVWHFKQLFDFRPROGFRQWHQWJXLDUV -HUHP\5HLPHU¦VDUWLFOH£$+LVWRU\RIWKH*8,¤LVDFRQFLVHZDONWKURXJKRIWKH QRWDEOHPLOHVWRQHVLQWKHWLPHOLQHRIWKH*8,OHDGLQJXSWRPRGHUQWLPHV:KLOH QRWWHFKQLFDOLWGRHVDJRRGMRERIKLJKOLJKWLQJWKHNH\FRQFHSWVWKDWDUHQRZ IXQGDPHQWDODQGIDPLOLDUSDUWVRIWKHFRPSXWHUV\VWHPVZHLQWHUDFWZLWKHYHU\GD\ KWWSDUWLFOHVVLWHSRLQWFRPSULQWUHDOKLVWRU\JXL 0LNH7XFN¦VHQWHUWDLQLQJDUWLFOH£7KH5HDO+LVWRU\RIWKH*8,¤JLYHVDQRYHUYLHZ RIWKHFDUHHUSDWKVRIVRPHRIWKHPDLQSOD\HUVLQWKHGHYHORSPHQWRIWKH*8,DV ZHNQRZLWWRGD\DQGDOVROLVWVVRPHOLQNVWRLQWHUHVWLQJUHODWHGDUWLFOHV KWWSLQYLVLEOHLVODQGQHW[WHUPFWOVHTVFWOVHTVKWPO £;WHUP &RQWURO 6HTXHQFHV¤ LV D OLVW RI WKH FRPPDQG VHTXHQFHV UHFRJQL]HG E\ ;WHUP2ULJLQDOO\FRPSLOHGE\(GZDUG0D\RIWKH8QLYHUVLW\RI&DOLIRUQLDDW%HU NHOH\LWLVWHUVHEXWFKRFNIXOORIXVHIXOLQIRUPDWLRQ,I\RXSODQRQZRUNLQJSUL PDULO\LQD/LQX[HQYLURQPHQWZLWK;WHUPZLQGRZV\RXUHDOO\VKRXOGKDYHWKLV GRFXPHQWKDQG\ KWWSZZZHFPDLQWHUQDWLRQDORUJSXEOLFDWLRQVVWDQGDUGV(FPDKWP 7KH(&0$VSHFLILFDWLRQLVDYDLODEOHIRUGRZQORDGDVD3')ILOHIURP(&0$ ,QWHUQDWLRQDODWWKLV85/,WDGGUHVVHVQRWRQO\$16,W\SHFRQWUROVHTXHQFHVEXW DOVRWKH$6&,,FKDUDFWHUVHW$WRYHUSDJHVLW¦VQRWDOLJKWUHDGEXWWKHGHWDLOHG JORVVDU\DQGUHIHUHQFHVWRRWKHU(&0$VWDQGDUGVPDNHLWZRUWKWKHGRZQORDG KWWSZZZFDWERUJaHVUZULWLQJVWDRXXWDRXXKWPO 7KHHQWLUHWH[WRI(ULF5D\PRQG¦V7KH$UWRI8QL[8VDELOLW\LVDYDLODEOHRQOLQHDW WKLV85/8QIRUWXQDWHO\,FRXOGQ¦WORFDWHD3')YHUVLRQDQG,OLNHWRUHDGIURP SDSHUUDWKHUWKDQVTXLQWLQJDWDVFUHHQ0U5D\PRQGSURYLGHVVRPHGHHSLQVLJKWV SHSSHUHGZLWKDQRFFDVLRQDOSRNHDWYDULRXVFRPSDQLHVDQGRSHUDWLQJV\VWHPV DQGH[SORUHVWKHKLVWRU\DQGUHDVRQLQJEHKLQGXVHULQWHUIDFHVIURPHDUO\NH\SXQFK DQGWHOHW\SHPDFKLQHVWKURXJKWRWKHSUHVHQWGD\7KHVHFWLRQ£5XOHVRI8VDELOLW\¤ FRQWDLQVVRPHUHDOJHPVWKDWWRRRIWHQVHHPWREHPLVVLQJLQXVHULQWHUIDFHGHVLJQ 7KHTXRWHDWWKHVWDUWRIWKLVFKDSWHULVRQHRIWKHUXOHVDOWKRXJK,GRQ¦WNQRZLI LWRULJLQDWHGZLWK0U5D\PRQGRUQRW KWWSZZZMRHORQVRIWZDUHFRPDUWLFOHV%LFXOWXUDOLVPKWPO ,QKLVEORJ-RHORQ6RIWZDUH-RHO6SROVN\RIIHUVDQLQWHOOLJHQWDQGLQVLJKWIXOUHYLHZ RI(ULF5D\PRQG¦V7KH$UWRI81,;3URJUDPPLQJ WKHSUHGHFHVVRUWR7KH$UWRI 8QL[ 8VDELOLW\  $OWKRXJK RULHQWHG WRZDUG ZRUNLQJ VRIWZDUH GHYHORSHUV WKH

Suggested Reading | 545 UHYLHZSURYLGHVVRPHLQWHUHVWLQJLQVLJKWVLQWRWKHFXOWXUDOGLIIHUHQFHVEHWZHHQWKH ZRUOGVRI8QL[DQG:LQGRZV$OWKRXJK3\WKRQKDVHYROYHGLQWRDODQJXDJHWKDW PDQDJHVWRVWUDGGOHERWKRIWKRVHZRUOGVDQGVPRRWKRXWDORWRIWKHGLIIHUHQFHV LQWKHXVHUH[SHULHQFHLWLVZRUWKZKLOHWRWDNHWKHWLPHWRXQGHUVWDQGZK\IURP DXVHU¦VSHUVSHFWLYH8QL[ DQG/LQX[ LVOLNHWKHFRFNSLWRIWKHVSDFHVKXWWOHDQG :LQGRZV LV D FRPIRUWDEOH IRXUGRRU VHGDQ ZLWK HOHFWULF ZLQGRZV DXWRPDWLF WUDQVPLVVLRQDQGPRUHNQREVDQGEXWWRQVRQWKHVWHUHRWKDQRQWKHGDVK(YHQ LI\RXGRQ¦WSODQRQPDNLQJDFDUHHURXWRIVRIWZDUHGHYHORSPHQW\RXZLOOFRPH DZD\IURPWKLVHVVD\ZLWKDEHWWHUXQGHUVWDQGLQJRIZK\WKLQJVDUHWKHZD\WKH\ DUHZKHQLWFRPHVWRPRGHUQRSHUDWLQJV\VWHPVDQGWKHLUXVHULQWHUIDFHV KWWSZZZS\WKRQZDUHFRPOLEUDU\WNLQWHU $FRPELQDWLRQRIWXWRULDOLQWURGXFWLRQDQGUHIHUHQFHWKLVZHEEDVHGGRFXPHQW E\)UHGULN/XQGKSUHVHQWVDVROLGLQWURGXFWLRQWR7N,QWHU,WDOVRDYDLODEOHDVD 3')GRFXPHQWDOWKRXJKLQDQRGGOD\RXWDVDWZRXSODQGVFDSHGRFXPHQW8Q IRUWXQDWHO\WKLVUHQGHUVLWEDVLFDOO\XVHOHVVIRUSULQWLQJDQGVDYLQJLQDFRPPRQ ULQJELQGHU%XWIRUPDWWLQJLVVXHVDVLGHWKHH[DPSOHVDUHJHQHUDOO\ZHOOFKRVHQ DQGHDV\WRIROORZ,I\RXKDYHQRRWKHUPDWHULDORQ7N,QWHUEXWWKLVWKH107 UHIHUHQFHOLVWHGEHORZDQGWKH3\WKRQGRFXPHQWDWLRQ\RXVKRXOGEHDEOHWRFUHDWH XVDEOH7N,QWHU*8,VZLWKRXWDQ\PDMRUGLIILFXOWLHV KWWSLQIRKRVWQPWHGXWFFKHOSSXEVWNLQWHU 7KHRQOLQHGRFXPHQW£7NLQWHU5HIHUHQFH$*8,IRU3\WKRQ¤LVDYDLODEOHIURP 1HZ0H[LFR7HFKQLFDO8QLYHUVLW\DWWKHDERYH85/,WLVDOVRDYDLODEOHLQ3') IRUPDWIURPWKHVDPHORFDWLRQ$OWKRXJKQRWH[KDXVWLYHWKLVLVDVXEVWDQWLDOUHI HUHQFH SDJHVLQ3')IRUPDW WKDWFRYHUVWRSLFVVXFKDVOD\RXWPHWKRGVDW WULEXWHVDQGWKHYDULRXVFRPPRQ7NZLGJHWVLQFOXGLQJ7N¦VUDWKHUXQLTXHFDQYDV ZLGJHW

546 | Chapter 13:ಗUser Interfaces