/Demo/tkinter/guido/ShellWindow.py

http://unladen-swallow.googlecode.com/ · Python · 147 lines · 128 code · 18 blank · 1 comment · 22 complexity · ccb9662739f61d45ba3b01c0f47a556e MD5 · raw file

  1. import os
  2. import sys
  3. import string
  4. from Tkinter import *
  5. from ScrolledText import ScrolledText
  6. from Dialog import Dialog
  7. import signal
  8. BUFSIZE = 512
  9. class ShellWindow(ScrolledText):
  10. def __init__(self, master=None, shell=None, **cnf):
  11. if not shell:
  12. try:
  13. shell = os.environ['SHELL']
  14. except KeyError:
  15. shell = '/bin/sh'
  16. shell = shell + ' -i'
  17. args = string.split(shell)
  18. shell = args[0]
  19. apply(ScrolledText.__init__, (self, master), cnf)
  20. self.pos = '1.0'
  21. self.bind('<Return>', self.inputhandler)
  22. self.bind('<Control-c>', self.sigint)
  23. self.bind('<Control-t>', self.sigterm)
  24. self.bind('<Control-k>', self.sigkill)
  25. self.bind('<Control-d>', self.sendeof)
  26. self.pid, self.fromchild, self.tochild = spawn(shell, args)
  27. self.tk.createfilehandler(self.fromchild, READABLE,
  28. self.outputhandler)
  29. def outputhandler(self, file, mask):
  30. data = os.read(file, BUFSIZE)
  31. if not data:
  32. self.tk.deletefilehandler(file)
  33. pid, sts = os.waitpid(self.pid, 0)
  34. print 'pid', pid, 'status', sts
  35. self.pid = None
  36. detail = sts>>8
  37. cause = sts & 0xff
  38. if cause == 0:
  39. msg = "exit status %d" % detail
  40. else:
  41. msg = "killed by signal %d" % (cause & 0x7f)
  42. if cause & 0x80:
  43. msg = msg + " -- core dumped"
  44. Dialog(self.master,
  45. text=msg,
  46. title="Exit status",
  47. bitmap='warning',
  48. default=0,
  49. strings=('OK',))
  50. return
  51. self.insert(END, data)
  52. self.pos = self.index("end - 1 char")
  53. self.yview_pickplace(END)
  54. def inputhandler(self, *args):
  55. if not self.pid:
  56. self.no_process()
  57. return "break"
  58. self.insert(END, "\n")
  59. line = self.get(self.pos, "end - 1 char")
  60. self.pos = self.index(END)
  61. os.write(self.tochild, line)
  62. return "break"
  63. def sendeof(self, *args):
  64. if not self.pid:
  65. self.no_process()
  66. return "break"
  67. os.close(self.tochild)
  68. return "break"
  69. def sendsig(self, sig):
  70. if not self.pid:
  71. self.no_process()
  72. return "break"
  73. os.kill(self.pid, sig)
  74. return "break"
  75. def sigint(self, *args):
  76. return self.sendsig(signal.SIGINT)
  77. def sigquit(self, *args):
  78. return self.sendsig(signal.SIGQUIT)
  79. def sigterm(self, *args):
  80. return self.sendsig(signal.SIGTERM)
  81. def sigkill(self, *args):
  82. return self.sendsig(signal.SIGKILL)
  83. def no_process(self):
  84. Dialog(self.master,
  85. text="No active process",
  86. title="No process",
  87. bitmap='error',
  88. default=0,
  89. strings=('OK',))
  90. MAXFD = 100 # Max number of file descriptors (os.getdtablesize()???)
  91. def spawn(prog, args):
  92. p2cread, p2cwrite = os.pipe()
  93. c2pread, c2pwrite = os.pipe()
  94. pid = os.fork()
  95. if pid == 0:
  96. # Child
  97. for i in 0, 1, 2:
  98. try:
  99. os.close(i)
  100. except os.error:
  101. pass
  102. if os.dup(p2cread) <> 0:
  103. sys.stderr.write('popen2: bad read dup\n')
  104. if os.dup(c2pwrite) <> 1:
  105. sys.stderr.write('popen2: bad write dup\n')
  106. if os.dup(c2pwrite) <> 2:
  107. sys.stderr.write('popen2: bad write dup\n')
  108. os.closerange(3, MAXFD)
  109. try:
  110. os.execvp(prog, args)
  111. finally:
  112. sys.stderr.write('execvp failed\n')
  113. os._exit(1)
  114. os.close(p2cread)
  115. os.close(c2pwrite)
  116. return pid, c2pread, p2cwrite
  117. def test():
  118. shell = string.join(sys.argv[1:])
  119. root = Tk()
  120. root.minsize(1, 1)
  121. if shell:
  122. w = ShellWindow(root, shell=shell)
  123. else:
  124. w = ShellWindow(root)
  125. w.pack(expand=1, fill=BOTH)
  126. w.focus_set()
  127. w.tk.mainloop()
  128. if __name__ == '__main__':
  129. test()