LILYPONDPREFIX -> LILYPOND_DATADIR
[lilypad-macos.git] / ProcessLog.py
blob08e8a4078c95b4c60e32738d213695b2215b492b
1 import objc
2 from Foundation import *
3 from AppKit import *
4 from PyObjCTools import NibClassBuilder, AppHelper
6 import subprocess
7 import os
8 import signal
10 NibClassBuilder.extractClasses("ProcessLog")
12 debug = 1
14 # class defined in ProcessLog.nib
15 class ProcessLog(NibClassBuilder.AutoBaseClass):
17 def init_(self):
18 self = self.init()
20 self.process = None
21 self.out_str = ''
23 return self
25 # the actual base class is NSObject
26 def setProcess (self, process):
27 self.out_str = ''
28 if self.isLive():
29 self.killProcess ()
31 self.process = process
33 def getNewOutput (self):
34 out = self.process.stdout
35 fd = out.fileno()
36 size = 1024
37 str = ''
38 while True:
39 s = os.read (fd, size)
40 str += s
41 if (len (s) == size):
42 size *= 2
43 else:
44 break
46 self.out_str += str
47 return str
49 def killProcess (self):
50 if self.process:
51 os.kill (self.process.pid, signal.SIGINT)
53 def isLive (self):
54 if not self.process:
55 return False
57 return self.process.poll() == None
59 # class defined in ProcessLog.nib
60 class ProcessLogWindowController (NibClassBuilder.AutoBaseClass):
61 # the actual base class is NSWindowController
62 # The following outlets are added to the class:
64 # window
65 # textView
66 # processLog
67 # cancelButton
68 # throbber
71 def __new__(cls):
72 # "Pythonic" constructor
73 return cls.alloc().initEmpty_()
75 def initEmpty_(self):
76 self = self.initWithWindowNibName_("ProcessLog")
77 self.setWindowTitle_('Process')
78 self.close_callback = None
79 self.processLog = ProcessLog.alloc().init_()
80 self.window().makeFirstResponder_(self.textView)
81 self.showWindow_(self)
83 # The window controller doesn't need to be retained (referenced)
84 # anywhere, so we pretend to have a reference to ourselves to avoid
85 # being garbage collected before the window is closed. The extra
86 # reference will be released in self.windowWillClose_()
87 self.retain()
88 return self
90 def windowWillClose_(self, notification):
92 ## UGH.
93 if self.close_callback:
94 self.close_callback (self)
95 self.cancelProcess_ (self)
96 self.suicide()
98 def suicide (self):
99 # see comment in self.initWithObject_()
100 self.autorelease()
102 def setWindowTitle_(self, title):
103 self.window().setTitle_(title)
105 def cancelProcess_(self, sender):
106 if self.processLog.isLive ():
107 self.processLog.killProcess ()
108 # rest is handled by timer.
110 def runProcessWithCallback (self, process, finish_callback):
111 self.finish_callback = finish_callback
112 self.processLog.setProcess (process)
114 self.cancelButton.setEnabled_ (True)
115 self.throbber.setUsesThreadedAnimation_ (True)
116 self.throbber.startAnimation_ (self)
117 self.setTimer ()
119 def setTimer (self):
120 self.timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_ \
121 (1.0/10.0, self, "timerCallback:", 0, 0)
123 def timerCallback_ (self, userinfo):
124 self.updateLog_ (None)
126 if self.processLog.isLive ():
127 self.setTimer ()
128 else:
129 self.finish ()
132 def finish (self):
133 self.updateLog_(None)
134 self.cancelButton.setEnabled_ (False)
135 self.throbber.stopAnimation_ (self)
137 cb = self.finish_callback
138 if cb <> None:
139 cb (self)
142 def clearLog_ (self, sender):
143 tv = self.textView
144 ts_len = tv.textStorage().length ()
145 range = NSRange()
146 range.location = 0
147 range.length = ts_len
148 tv.replaceCharactersInRange_withString_ (range, '')
150 def addText (self, str):
151 tv = self.textView
152 ts_len = tv.textStorage().length ()
154 range = NSRange()
155 range.location = ts_len
156 range.length = 0
157 tv.replaceCharactersInRange_withString_ (range, str)
158 range.length = len (str)
159 tv.scrollRangeToVisible_ (range)
161 def updateLog_ (self, sender):
162 str = self.processLog.getNewOutput ()
163 self.addText (str)
166 if __name__ == "__main__":
167 AppHelper.runEventLoop()