| #! /usr/bin/env python |
| |
| # Watch line printer queues (only works with BSD 4.3 lpq). |
| # |
| # This brings up a window containing one line per printer argument. |
| # |
| # Each line gives a small summary of the printer's status and queue. |
| # The status tries to give as much relevant information as possible, |
| # and gives extra info if you have jobs in the queue. |
| # |
| # The line's background color gives a hint at the status: navajo white |
| # for idle, green if your job is now printing, yellow/orange for |
| # small/large queue, red for errors. |
| # |
| # To reduce the duration of the unresponsive time while it is waiting |
| # for an lpq subprocess to finish, it polls one printer every |
| # delay/len(printers) seconds. A tiny dot indicates the last printer |
| # updated. Hit the mouse button in the window to update the next one. |
| # |
| # To do: |
| # - add an argument to override the default delay |
| # - add arguments to override the default colors |
| # - better heuristic for small/large queue (and more colors!) |
| # - mouse clicks should update the printer clicked in |
| # - better visual appearance, e.g., boxes around the lines? |
| |
| import posix |
| import sys |
| import time |
| import string |
| |
| import stdwin |
| from stdwinevents import * |
| import mainloop |
| |
| # Default parameters |
| DEF_PRINTER = 'oce' # This is CWI specific! |
| DEF_DELAY = 10 |
| |
| # Color assignments |
| c_unknown = stdwin.fetchcolor('white') |
| c_idle = stdwin.fetchcolor('navajo white') |
| c_ontop = stdwin.fetchcolor('green') |
| c_smallqueue = stdwin.fetchcolor('yellow') |
| c_bigqueue = stdwin.fetchcolor('orange') |
| c_error = stdwin.fetchcolor('red') |
| |
| def main(): |
| delay = DEF_DELAY |
| # |
| try: |
| thisuser = posix.environ['LOGNAME'] |
| except: |
| thisuser = posix.environ['USER'] |
| # |
| printers = sys.argv[1:] |
| if printers: |
| # Strip '-P' from printer names just in case |
| # the user specified it... |
| for i in range(len(printers)): |
| if printers[i][:2] == '-P': |
| printers[i] = printers[i][2:] |
| else: |
| if posix.environ.has_key('PRINTER'): |
| printers = [posix.environ['PRINTER']] |
| else: |
| printers = [DEF_PRINTER] |
| # |
| width = stdwin.textwidth('in')*20 |
| height = len(printers) * stdwin.lineheight() + 5 |
| stdwin.setdefwinsize(width, height) |
| stdwin.setdefscrollbars(0, 0) |
| # |
| win = stdwin.open('lpwin') |
| # |
| win.printers = printers |
| win.colors = [c_unknown] * len(printers) |
| win.texts = printers[:] |
| win.next = 0 |
| win.delay = DEF_DELAY |
| win.thisuser = thisuser |
| win.dispatch = lpdispatch |
| # |
| win.settimer(1) |
| # |
| mainloop.register(win) |
| mainloop.mainloop() |
| |
| def lpdispatch(event): |
| type, win, detail = event |
| if type == WE_CLOSE or type == WE_CHAR and detail in ('q', 'Q'): |
| mainloop.unregister(win) |
| elif type == WE_DRAW: |
| drawproc(win) |
| elif type == WE_TIMER: |
| update(win) |
| win.change((0,0), (10000, 10000)) |
| elif type == WE_MOUSE_UP: |
| win.settimer(1) |
| |
| def drawproc(win): |
| d = win.begindrawing() |
| offset = d.textwidth('.') |
| h, v = 0, 0 |
| for i in range(len(win.printers)): |
| text = win.texts[i] |
| color = win.colors[i] |
| d.setbgcolor(color) |
| d.erase((h, v), (h+10000, v+d.lineheight())) |
| if (i+1) % len(win.printers) == win.next and color <> c_unknown: |
| d.text((h, v), '.') |
| d.text((h+offset, v), text) |
| v = v + d.lineheight() |
| |
| def update(win): |
| i = win.next |
| win.next = (i+1) % len(win.printers) |
| win.texts[i], win.colors[i] = makestatus(win.printers[i], win.thisuser) |
| win.settimer(int(win.delay * 10.0 / len(win.printers))) |
| |
| def makestatus(name, thisuser): |
| pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r') |
| lines = [] |
| users = {} |
| aheadbytes = 0 |
| aheadjobs = 0 |
| userseen = 0 |
| totalbytes = 0 |
| totaljobs = 0 |
| color = c_unknown |
| while 1: |
| line = pipe.readline() |
| if not line: break |
| fields = string.split(line) |
| n = len(fields) |
| if len(fields) >= 6 and fields[n-1] == 'bytes': |
| rank = fields[0] |
| user = fields[1] |
| job = fields[2] |
| files = fields[3:-2] |
| bytes = eval(fields[n-2]) |
| if user == thisuser: |
| userseen = 1 |
| if aheadjobs == 0: |
| color = c_ontop |
| elif not userseen: |
| aheadbytes = aheadbytes + bytes |
| aheadjobs = aheadjobs + 1 |
| totalbytes = totalbytes + bytes |
| totaljobs = totaljobs + 1 |
| if color == c_unknown: |
| color = c_smallqueue |
| elif color == c_smallqueue: |
| color = c_bigqueue |
| if users.has_key(user): |
| ujobs, ubytes = users[user] |
| else: |
| ujobs, ubytes = 0, 0 |
| ujobs = ujobs + 1 |
| ubytes = ubytes + bytes |
| users[user] = ujobs, ubytes |
| else: |
| if fields and fields[0] <> 'Rank': |
| line = string.strip(line) |
| if line == 'no entries': |
| line = name + ': idle' |
| if color == c_unknown: |
| color = c_idle |
| elif line[-22:] == ' is ready and printing': |
| line = line[:-22] |
| else: |
| line = name + ': ' + line |
| color = c_error |
| lines.append(line) |
| # |
| if totaljobs: |
| line = `(totalbytes+1023)/1024` + ' K' |
| if totaljobs <> len(users): |
| line = line + ' (' + `totaljobs` + ' jobs)' |
| if len(users) == 1: |
| line = line + ' for ' + users.keys()[0] |
| else: |
| line = line + ' for ' + `len(users)` + ' users' |
| if userseen: |
| if aheadjobs == 0: |
| line = line + ' (' + thisuser + ' first)' |
| else: |
| line = line + ' (' + `(aheadbytes+1023)/1024` |
| line = line + ' K before ' + thisuser + ')' |
| lines.append(line) |
| # |
| sts = pipe.close() |
| if sts: |
| lines.append('lpq exit status ' + `sts`) |
| color = c_error |
| return string.joinfields(lines, ': '), color |
| |
| main() |