Guido van Rossum | 93a35f4 | 1992-12-14 14:12:10 +0000 | [diff] [blame] | 1 | #! /usr/local/bin/python |
Guido van Rossum | 9cf8f33 | 1992-03-30 10:54:51 +0000 | [diff] [blame] | 2 | |
| 3 | # Watch line printer queues (only works with BSD 4.3 lpq). |
| 4 | # |
| 5 | # This brings up a window containing one line per printer argument. |
| 6 | # |
| 7 | # Each line gives a small summary of the printer's status and queue. |
| 8 | # The status tries to give as much relevant information as possible, |
| 9 | # and gives extra info if you have jobs in the queue. |
| 10 | # |
| 11 | # The line's background color gives a hint at the status: navajo white |
| 12 | # for idle, green if your job is now printing, yellow/orange for |
| 13 | # small/large queue, red for errors. |
| 14 | # |
| 15 | # To reduce the duration of the unresponsive time while it is waiting |
| 16 | # for an lpq subprocess to finish, it polls one printer every |
| 17 | # delay/len(printers) seconds. A tiny dot indicates the last printer |
| 18 | # updated. Hit the mouse button in the window to update the next one. |
| 19 | # |
| 20 | # To do: |
| 21 | # - add an argument to override the default delay |
| 22 | # - add arguments to override the default colors |
| 23 | # - better heuristic for small/large queue (and more colors!) |
| 24 | # - mouse clicks should update the printer clicked in |
| 25 | # - better visual appearance, e.g., boxes around the lines? |
| 26 | |
| 27 | import posix |
| 28 | import sys |
| 29 | import time |
| 30 | import string |
| 31 | |
| 32 | import stdwin |
| 33 | from stdwinevents import * |
| 34 | import mainloop |
| 35 | |
| 36 | # Default parameters |
| 37 | DEF_PRINTER = 'oce' # This is CWI specific! |
| 38 | DEF_DELAY = 10 |
| 39 | |
| 40 | # Color assignments |
| 41 | c_unknown = stdwin.fetchcolor('white') |
| 42 | c_idle = stdwin.fetchcolor('navajo white') |
| 43 | c_ontop = stdwin.fetchcolor('green') |
| 44 | c_smallqueue = stdwin.fetchcolor('yellow') |
| 45 | c_bigqueue = stdwin.fetchcolor('orange') |
| 46 | c_error = stdwin.fetchcolor('red') |
| 47 | |
| 48 | def main(): |
| 49 | delay = DEF_DELAY |
| 50 | # |
| 51 | try: |
| 52 | thisuser = posix.environ['LOGNAME'] |
| 53 | except: |
| 54 | thisuser = posix.environ['USER'] |
| 55 | # |
| 56 | printers = sys.argv[1:] |
| 57 | if printers: |
| 58 | # Strip '-P' from printer names just in case |
| 59 | # the user specified it... |
| 60 | for i in range(len(printers)): |
| 61 | if printers[i][:2] == '-P': |
| 62 | printers[i] = printers[i][2:] |
| 63 | else: |
| 64 | if posix.environ.has_key('PRINTER'): |
| 65 | printers = [posix.environ['PRINTER']] |
| 66 | else: |
| 67 | printers = [DEF_PRINTER] |
| 68 | # |
| 69 | width = stdwin.textwidth('in')*20 |
| 70 | height = len(printers) * stdwin.lineheight() + 5 |
| 71 | stdwin.setdefwinsize(width, height) |
| 72 | stdwin.setdefscrollbars(0, 0) |
| 73 | # |
| 74 | win = stdwin.open('lpwin') |
| 75 | # |
| 76 | win.printers = printers |
| 77 | win.colors = [c_unknown] * len(printers) |
| 78 | win.texts = printers[:] |
| 79 | win.next = 0 |
| 80 | win.delay = DEF_DELAY |
| 81 | win.thisuser = thisuser |
| 82 | win.dispatch = lpdispatch |
| 83 | # |
| 84 | win.settimer(1) |
| 85 | # |
| 86 | mainloop.register(win) |
| 87 | mainloop.mainloop() |
| 88 | |
Guido van Rossum | d55f4d1 | 1993-12-17 14:39:12 +0000 | [diff] [blame] | 89 | def lpdispatch(event): |
| 90 | type, win, detail = event |
Guido van Rossum | 9cf8f33 | 1992-03-30 10:54:51 +0000 | [diff] [blame] | 91 | if type == WE_CLOSE or type == WE_CHAR and detail in ('q', 'Q'): |
| 92 | mainloop.unregister(win) |
| 93 | elif type == WE_DRAW: |
| 94 | drawproc(win) |
| 95 | elif type == WE_TIMER: |
| 96 | update(win) |
| 97 | win.change((0,0), (10000, 10000)) |
| 98 | elif type == WE_MOUSE_UP: |
| 99 | win.settimer(1) |
| 100 | |
| 101 | def drawproc(win): |
| 102 | d = win.begindrawing() |
| 103 | offset = d.textwidth('.') |
| 104 | h, v = 0, 0 |
| 105 | for i in range(len(win.printers)): |
| 106 | text = win.texts[i] |
| 107 | color = win.colors[i] |
| 108 | d.setbgcolor(color) |
| 109 | d.erase((h, v), (h+10000, v+d.lineheight())) |
| 110 | if (i+1) % len(win.printers) == win.next and color <> c_unknown: |
| 111 | d.text((h, v), '.') |
| 112 | d.text((h+offset, v), text) |
| 113 | v = v + d.lineheight() |
| 114 | |
| 115 | def update(win): |
| 116 | i = win.next |
| 117 | win.next = (i+1) % len(win.printers) |
| 118 | win.texts[i], win.colors[i] = makestatus(win.printers[i], win.thisuser) |
| 119 | win.settimer(int(win.delay * 10.0 / len(win.printers))) |
| 120 | |
| 121 | def makestatus(name, thisuser): |
| 122 | pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r') |
| 123 | lines = [] |
| 124 | users = {} |
| 125 | aheadbytes = 0 |
| 126 | aheadjobs = 0 |
| 127 | userseen = 0 |
| 128 | totalbytes = 0 |
| 129 | totaljobs = 0 |
| 130 | color = c_unknown |
| 131 | while 1: |
| 132 | line = pipe.readline() |
| 133 | if not line: break |
| 134 | fields = string.split(line) |
| 135 | n = len(fields) |
| 136 | if len(fields) >= 6 and fields[n-1] == 'bytes': |
| 137 | rank = fields[0] |
| 138 | user = fields[1] |
| 139 | job = fields[2] |
| 140 | files = fields[3:-2] |
| 141 | bytes = eval(fields[n-2]) |
| 142 | if user == thisuser: |
| 143 | userseen = 1 |
| 144 | if aheadjobs == 0: |
| 145 | color = c_ontop |
| 146 | elif not userseen: |
| 147 | aheadbytes = aheadbytes + bytes |
| 148 | aheadjobs = aheadjobs + 1 |
| 149 | totalbytes = totalbytes + bytes |
| 150 | totaljobs = totaljobs + 1 |
| 151 | if color == c_unknown: |
| 152 | color = c_smallqueue |
| 153 | elif color == c_smallqueue: |
| 154 | color = c_bigqueue |
| 155 | if users.has_key(user): |
| 156 | ujobs, ubytes = users[user] |
| 157 | else: |
| 158 | ujobs, ubytes = 0, 0 |
| 159 | ujobs = ujobs + 1 |
| 160 | ubytes = ubytes + bytes |
| 161 | users[user] = ujobs, ubytes |
| 162 | else: |
| 163 | if fields and fields[0] <> 'Rank': |
| 164 | line = string.strip(line) |
| 165 | if line == 'no entries': |
| 166 | line = name + ': idle' |
| 167 | if color == c_unknown: |
| 168 | color = c_idle |
| 169 | elif line[-22:] == ' is ready and printing': |
| 170 | line = line[:-22] |
| 171 | else: |
| 172 | line = name + ': ' + line |
| 173 | color = c_error |
| 174 | lines.append(line) |
| 175 | # |
| 176 | if totaljobs: |
| 177 | line = `(totalbytes+1023)/1024` + ' K' |
| 178 | if totaljobs <> len(users): |
| 179 | line = line + ' (' + `totaljobs` + ' jobs)' |
| 180 | if len(users) == 1: |
| 181 | line = line + ' for ' + users.keys()[0] |
| 182 | else: |
| 183 | line = line + ' for ' + `len(users)` + ' users' |
| 184 | if userseen: |
| 185 | if aheadjobs == 0: |
| 186 | line = line + ' (' + thisuser + ' first)' |
| 187 | else: |
| 188 | line = line + ' (' + `(aheadbytes+1023)/1024` |
| 189 | line = line + ' K before ' + thisuser + ')' |
| 190 | lines.append(line) |
| 191 | # |
| 192 | sts = pipe.close() |
| 193 | if sts: |
| 194 | lines.append('lpq exit status ' + `sts`) |
| 195 | color = c_error |
| 196 | return string.joinfields(lines, ': '), color |
| 197 | |
| 198 | main() |