As I mentioned
before, we got a Mac Mini. It's been a bit of a pain getting it all
set up, but I think we're finally done. The web server is moved over,
along with all the websites. The mail server is moved over, and I took
the opportunity to delete my mother's account. (That's not as bad as it
sounds. She had it redirecting to GMail anyways, and the error message
says as much. Of course, since I haven't told her about it yet, this
will probably come as a bit of a surprise.) Uh, and that turned out to
be all the servers I was running. Well, I was running CherryPy, and
some other stuff, but it wasn't particularly important, and I've found
better ways of doing it, for the most part.
But that's not really why I'm posting here. I'm posting here because
I got some cool Python stuff working under the Mac, and I wanted to
share it. Specifically, I (or rather, my wife and I) wanted to have a
window showing on the login screen, displaying the
weather. After several bits of trial and error, and liberal
"borrowing" from Dethe Elza's Pastels project I
finally got something which would do what I wanted. First, the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 | from AppKit import NSObject, NSApplication, NSTimer, NSApp, NSURL
from AppKit import NSURLRequest, NSWindow, NSTitledWindowMask
from AppKit import NSClosableWindowMask, NSMiniaturizableWindowMask
from AppKit import NSResizableWindowMask, NSBackingStoreBuffered
from BeautifulSoup import BeautifulSoup
from PyObjCTools import AppHelper
from WebKit import WebView, WebDataSource
import os
import stat
import time
import urllib
width, height = 620, 540
def Window(title, width, height, view=None):
window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
((0,0),(width,height)),
NSTitledWindowMask |
NSClosableWindowMask |
NSMiniaturizableWindowMask |
NSResizableWindowMask,
NSBackingStoreBuffered,
False)
window.setTitle_(title)
if view:
window.setContentView_(view)
window.orderFront_(window)
return window
class MyAppDelegate(NSObject):
def update( self, timer ):
y = urllib.urlopen( str(self.wUrl) ).read()
b = BeautifulSoup( y )
self.view.mainFrame().loadHTMLString_baseURL_(
"""<html><head><title>test<title>%s<head>
<body>%s<body><html>""" %
("".join( [str(x) for x in b.head.findAll( 'style' )]), b.body.div.div.div.div.div),
self.wUrl )
def applicationDidFinishLaunching_(self, notification):
rect = ((0,0),(width, height))
self.view = WebView.alloc().initWithFrame_(rect)
self.window = Window('Pastels Test', width, height, self.view)
self.window_delegate = MyWindowDelegate.alloc().init()
self.window.setDelegate_(self.window_delegate)
self.wUrl = NSURL.alloc().initWithString_(
"http://weatheroffice.ec.gc.ca/city/pages/on-143_metric_e.html")
self.update( None )
timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
1800, self, 'update', None, True )
class MyWindowDelegate(NSObject):
def windowWillClose_(self, notification):
NSApp().terminate_(self)
def main():
app_delegate = MyAppDelegate.alloc().init()
NSApplication.sharedApplication().setDelegate_(app_delegate)
AppHelper.runEventLoop(installInterrupt=True)
if __name__ == "__main__":
while True:
if os.stat( "/dev/console" )[stat.ST_UID] == 0:
# If the console is owned by root, start the app.
main()
else:
# Otherwise, sleep until it is.
time.sleep( 10 )
|
And next, the launchd plist file that keeps it running:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.
com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>ca.latte.update.login.weather</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/test.py</string>
</array>
<key>ServiceDescription</key>
<string>A program to pop up the weather in a box</string>
<key>LowPriorityIO</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>OnDemand</key>
<false/>
<key>Nice</key>
<integer>1</integer>
<key>StandardOutPath</key>
<string>/path/to/loginWeather.out</string>
<key>StandardErrorPath</key>
<string>/path/to/loginWeather.err</string>
</dict>
</plist>
|
And there you have it. Way cooler than my previous attempt, which tried
to write using sudo defaults write
/Library/Preferences/com.apple.loginwindow LoginwindowText -string
"parsed weather info here".
If any of you have any questions, I'ld love to answer them as best I
can. Just comment, and I'll see what I can dig up. By which I mean
email Dethe, and ask him. ;)